{-# LANGUAGE DeriveAnyClass #-}
{-|
Module: IHP.DataSync.DynamicQuery
Description: The normal IHP query functionality is type-safe. This module provides type-unsafe access to the database.
Copyright: (c) digitally induced GmbH, 2021
-}
module IHP.DataSync.DynamicQuery where

import IHP.ControllerPrelude hiding (OrderByClause)
import Data.Aeson
import qualified Data.HashMap.Strict as HashMap
import qualified Database.PostgreSQL.Simple as PG
import qualified Database.PostgreSQL.Simple.FromField as PG
import qualified Database.PostgreSQL.Simple.FromRow as PG
import qualified Database.PostgreSQL.Simple.ToField as PG
import qualified Database.PostgreSQL.Simple.Types as PG
import qualified Database.PostgreSQL.Simple.Notification as PG
import qualified IHP.QueryBuilder as QueryBuilder
import Data.Aeson.TH
import qualified GHC.Generics
import qualified Control.DeepSeq as DeepSeq
import qualified Data.Aeson.KeyMap as Aeson
import qualified Data.Aeson.Key as Aeson

data Field = Field { Field -> Text
fieldName :: Text, Field -> DynamicValue
fieldValue :: DynamicValue }

data DynamicValue
    = IntValue !Int
    | DoubleValue !Double
    | TextValue !Text
    | BoolValue !Bool
    | UUIDValue !UUID
    | DateTimeValue !UTCTime
    | PointValue !Point
    | Null
    deriving (Int -> DynamicValue -> ShowS
[DynamicValue] -> ShowS
DynamicValue -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [DynamicValue] -> ShowS
$cshowList :: [DynamicValue] -> ShowS
show :: DynamicValue -> String
$cshow :: DynamicValue -> String
showsPrec :: Int -> DynamicValue -> ShowS
$cshowsPrec :: Int -> DynamicValue -> ShowS
Show, DynamicValue -> DynamicValue -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: DynamicValue -> DynamicValue -> Bool
$c/= :: DynamicValue -> DynamicValue -> Bool
== :: DynamicValue -> DynamicValue -> Bool
$c== :: DynamicValue -> DynamicValue -> Bool
Eq)

newtype UndecodedJSON = UndecodedJSON ByteString
    deriving (Int -> UndecodedJSON -> ShowS
[UndecodedJSON] -> ShowS
UndecodedJSON -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [UndecodedJSON] -> ShowS
$cshowList :: [UndecodedJSON] -> ShowS
show :: UndecodedJSON -> String
$cshow :: UndecodedJSON -> String
showsPrec :: Int -> UndecodedJSON -> ShowS
$cshowsPrec :: Int -> UndecodedJSON -> ShowS
Show, UndecodedJSON -> UndecodedJSON -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: UndecodedJSON -> UndecodedJSON -> Bool
$c/= :: UndecodedJSON -> UndecodedJSON -> Bool
== :: UndecodedJSON -> UndecodedJSON -> Bool
$c== :: UndecodedJSON -> UndecodedJSON -> Bool
Eq)

-- | Similiar to IHP.QueryBuilder.SQLQuery, but is designed to be accessed by external users
--
-- When compiling to SQL we have to be extra careful to escape all identifers and variables in the query.
-- The normal IHP.QueryBuilder doesn't need to be that careful as parts of the input are derived from
-- generated code from the Schema.sql.
--
data DynamicSQLQuery = DynamicSQLQuery
    { DynamicSQLQuery -> Text
table :: !Text
    , DynamicSQLQuery -> SelectedColumns
selectedColumns :: SelectedColumns
    , DynamicSQLQuery -> Maybe ConditionExpression
whereCondition :: !(Maybe ConditionExpression)
    , DynamicSQLQuery -> [OrderByClause]
orderByClause :: ![OrderByClause]
    , DynamicSQLQuery -> Maybe ByteString
distinctOnColumn :: !(Maybe ByteString)
    , DynamicSQLQuery -> Maybe Int
limit :: !(Maybe Int)
    , DynamicSQLQuery -> Maybe Int
offset :: !(Maybe Int)
    } deriving (Int -> DynamicSQLQuery -> ShowS
[DynamicSQLQuery] -> ShowS
DynamicSQLQuery -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [DynamicSQLQuery] -> ShowS
$cshowList :: [DynamicSQLQuery] -> ShowS
show :: DynamicSQLQuery -> String
$cshow :: DynamicSQLQuery -> String
showsPrec :: Int -> DynamicSQLQuery -> ShowS
$cshowsPrec :: Int -> DynamicSQLQuery -> ShowS
Show, DynamicSQLQuery -> DynamicSQLQuery -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: DynamicSQLQuery -> DynamicSQLQuery -> Bool
$c/= :: DynamicSQLQuery -> DynamicSQLQuery -> Bool
== :: DynamicSQLQuery -> DynamicSQLQuery -> Bool
$c== :: DynamicSQLQuery -> DynamicSQLQuery -> Bool
Eq)

data OrderByClause
    = OrderByClause
        { OrderByClause -> ByteString
orderByColumn :: !ByteString
        , OrderByClause -> OrderByDirection
orderByDirection :: !OrderByDirection }
    | OrderByTSRank { OrderByClause -> Text
tsvector :: Text, OrderByClause -> Text
tsquery :: !Text }
    deriving (Int -> OrderByClause -> ShowS
[OrderByClause] -> ShowS
OrderByClause -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [OrderByClause] -> ShowS
$cshowList :: [OrderByClause] -> ShowS
show :: OrderByClause -> String
$cshow :: OrderByClause -> String
showsPrec :: Int -> OrderByClause -> ShowS
$cshowsPrec :: Int -> OrderByClause -> ShowS
Show, OrderByClause -> OrderByClause -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: OrderByClause -> OrderByClause -> Bool
$c/= :: OrderByClause -> OrderByClause -> Bool
== :: OrderByClause -> OrderByClause -> Bool
$c== :: OrderByClause -> OrderByClause -> Bool
Eq, forall x. Rep OrderByClause x -> OrderByClause
forall x. OrderByClause -> Rep OrderByClause x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep OrderByClause x -> OrderByClause
$cfrom :: forall x. OrderByClause -> Rep OrderByClause x
GHC.Generics.Generic, OrderByClause -> ()
forall a. (a -> ()) -> NFData a
rnf :: OrderByClause -> ()
$crnf :: OrderByClause -> ()
DeepSeq.NFData)

-- | Represents a WHERE conditions of a 'DynamicSQLQuery'
data ConditionExpression
    = ColumnExpression { ConditionExpression -> Text
field :: !Text }
    | NullExpression
    | InfixOperatorExpression
        { ConditionExpression -> ConditionExpression
left :: !ConditionExpression
        , ConditionExpression -> ConditionOperator
op :: !ConditionOperator
        , ConditionExpression -> ConditionExpression
right :: !ConditionExpression
        }
    | LiteralExpression { ConditionExpression -> DynamicValue
value :: !DynamicValue }
    | CallExpression { ConditionExpression -> FunctionCall
functionCall :: !FunctionCall }
    | ListExpression { ConditionExpression -> [DynamicValue]
values :: ![DynamicValue] }
    deriving (Int -> ConditionExpression -> ShowS
[ConditionExpression] -> ShowS
ConditionExpression -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [ConditionExpression] -> ShowS
$cshowList :: [ConditionExpression] -> ShowS
show :: ConditionExpression -> String
$cshow :: ConditionExpression -> String
showsPrec :: Int -> ConditionExpression -> ShowS
$cshowsPrec :: Int -> ConditionExpression -> ShowS
Show, ConditionExpression -> ConditionExpression -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: ConditionExpression -> ConditionExpression -> Bool
$c/= :: ConditionExpression -> ConditionExpression -> Bool
== :: ConditionExpression -> ConditionExpression -> Bool
$c== :: ConditionExpression -> ConditionExpression -> Bool
Eq)

data FunctionCall
    = ToTSQuery { FunctionCall -> Text
text :: !Text } -- ^ to_tsquery('english', text)
    deriving (Int -> FunctionCall -> ShowS
[FunctionCall] -> ShowS
FunctionCall -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [FunctionCall] -> ShowS
$cshowList :: [FunctionCall] -> ShowS
show :: FunctionCall -> String
$cshow :: FunctionCall -> String
showsPrec :: Int -> FunctionCall -> ShowS
$cshowsPrec :: Int -> FunctionCall -> ShowS
Show, FunctionCall -> FunctionCall -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: FunctionCall -> FunctionCall -> Bool
$c/= :: FunctionCall -> FunctionCall -> Bool
== :: FunctionCall -> FunctionCall -> Bool
$c== :: FunctionCall -> FunctionCall -> Bool
Eq, forall x. Rep FunctionCall x -> FunctionCall
forall x. FunctionCall -> Rep FunctionCall x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep FunctionCall x -> FunctionCall
$cfrom :: forall x. FunctionCall -> Rep FunctionCall x
GHC.Generics.Generic, FunctionCall -> ()
forall a. (a -> ()) -> NFData a
rnf :: FunctionCall -> ()
$crnf :: FunctionCall -> ()
DeepSeq.NFData)

-- | Operators available in WHERE conditions
data ConditionOperator
    = OpEqual -- ^ a = b
    | OpGreaterThan -- ^ a > b
    | OpLessThan -- ^ a < b
    | OpGreaterThanOrEqual -- ^ a >= b
    | OpLessThanOrEqual -- ^ a <= b
    | OpNotEqual -- ^ a <> b
    | OpAnd -- ^ a AND b
    | OpOr -- ^ a OR b
    | OpIs -- ^ a IS b
    | OpIsNot -- ^ a IS NOT b
    | OpTSMatch -- ^ tsvec_a @@ tsvec_b
    | OpIn -- ^ a IN b
    deriving (Int -> ConditionOperator -> ShowS
[ConditionOperator] -> ShowS
ConditionOperator -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [ConditionOperator] -> ShowS
$cshowList :: [ConditionOperator] -> ShowS
show :: ConditionOperator -> String
$cshow :: ConditionOperator -> String
showsPrec :: Int -> ConditionOperator -> ShowS
$cshowsPrec :: Int -> ConditionOperator -> ShowS
Show, ConditionOperator -> ConditionOperator -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: ConditionOperator -> ConditionOperator -> Bool
$c/= :: ConditionOperator -> ConditionOperator -> Bool
== :: ConditionOperator -> ConditionOperator -> Bool
$c== :: ConditionOperator -> ConditionOperator -> Bool
Eq)

data SelectedColumns
    = SelectAll -- ^ SELECT * FROM table
    | SelectSpecific [Text] -- ^ SELECT a, b, c FROM table
    deriving (Int -> SelectedColumns -> ShowS
[SelectedColumns] -> ShowS
SelectedColumns -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [SelectedColumns] -> ShowS
$cshowList :: [SelectedColumns] -> ShowS
show :: SelectedColumns -> String
$cshow :: SelectedColumns -> String
showsPrec :: Int -> SelectedColumns -> ShowS
$cshowsPrec :: Int -> SelectedColumns -> ShowS
Show, SelectedColumns -> SelectedColumns -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: SelectedColumns -> SelectedColumns -> Bool
$c/= :: SelectedColumns -> SelectedColumns -> Bool
== :: SelectedColumns -> SelectedColumns -> Bool
$c== :: SelectedColumns -> SelectedColumns -> Bool
Eq)

instance FromJSON ByteString where
    parseJSON :: Value -> Parser ByteString
parseJSON (String Text
v) = forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ forall a b. ConvertibleStrings a b => a -> b
cs Text
v

instance FromJSON PG.Action where
    parseJSON :: Value -> Parser Action
parseJSON (String Text
v) = forall (f :: * -> *) a. Applicative f => a -> f a
pure (ByteString -> Action
PG.Escape (forall a b. ConvertibleStrings a b => a -> b
cs Text
v))

instance {-# OVERLAPS #-} ToJSON [Field] where
    toJSON :: [Field] -> Value
toJSON [Field]
fields = [Pair] -> Value
object (forall a b. (a -> b) -> [a] -> [b]
map (\Field { Text
fieldName :: Text
$sel:fieldName:Field :: Field -> Text
fieldName, DynamicValue
fieldValue :: DynamicValue
$sel:fieldValue:Field :: Field -> DynamicValue
fieldValue } -> (forall a b. ConvertibleStrings a b => a -> b
cs Text
fieldName) forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
.= (forall a. ToJSON a => a -> Value
toJSON DynamicValue
fieldValue)) [Field]
fields)
    toEncoding :: [Field] -> Encoding
toEncoding [Field]
fields = Series -> Encoding
pairs forall a b. (a -> b) -> a -> b
$ forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' forall a. Semigroup a => a -> a -> a
(<>) forall a. Monoid a => a
mempty [Series]
encodedFields
        where
            encodedFields :: [Series]
encodedFields = (forall a b. (a -> b) -> [a] -> [b]
map (\Field { Text
fieldName :: Text
$sel:fieldName:Field :: Field -> Text
fieldName, DynamicValue
fieldValue :: DynamicValue
$sel:fieldValue:Field :: Field -> DynamicValue
fieldValue } -> (forall a b. ConvertibleStrings a b => a -> b
cs Text
fieldName) forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
.= (forall a. ToJSON a => a -> Value
toJSON DynamicValue
fieldValue)) [Field]
fields)

instance ToJSON DynamicValue where
    toJSON :: DynamicValue -> Value
toJSON (IntValue Int
value) = forall a. ToJSON a => a -> Value
toJSON Int
value
    toJSON (DoubleValue Double
value) = forall a. ToJSON a => a -> Value
toJSON Double
value
    toJSON (TextValue Text
value) = forall a. ToJSON a => a -> Value
toJSON Text
value
    toJSON (BoolValue Bool
value) = forall a. ToJSON a => a -> Value
toJSON Bool
value
    toJSON (UUIDValue UUID
value) = forall a. ToJSON a => a -> Value
toJSON UUID
value
    toJSON (DateTimeValue UTCTime
value) = forall a. ToJSON a => a -> Value
toJSON UTCTime
value
    toJSON (PointValue Point
value) = forall a. ToJSON a => a -> Value
toJSON Point
value
    toJSON DynamicValue
IHP.DataSync.DynamicQuery.Null = forall a. ToJSON a => a -> Value
toJSON Value
Data.Aeson.Null

instance PG.FromField Field where
    fromField :: FieldParser Field
fromField Field
field Maybe ByteString
fieldValue' = do
            DynamicValue
fieldValue <- Conversion DynamicValue
fieldValue
            forall (f :: * -> *) a. Applicative f => a -> f a
pure Field { Text
DynamicValue
fieldName :: Text
fieldValue :: DynamicValue
$sel:fieldValue:Field :: DynamicValue
$sel:fieldName:Field :: Text
.. }
        where
            fieldName :: Text
fieldName = (Field -> Maybe ByteString
PG.name Field
field)
                forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Text -> Text
columnNameToFieldName forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. forall a b. ConvertibleStrings a b => a -> b
cs)
                forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> forall a. a -> Maybe a -> a
fromMaybe Text
""
            fieldValue :: Conversion DynamicValue
fieldValue =
                    (Int -> DynamicValue
IntValue forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a. FromField a => FieldParser a
PG.fromField Field
field Maybe ByteString
fieldValue')
                forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> (Text -> DynamicValue
TextValue forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a. FromField a => FieldParser a
PG.fromField Field
field Maybe ByteString
fieldValue')
                forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> (Bool -> DynamicValue
BoolValue forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a. FromField a => FieldParser a
PG.fromField Field
field Maybe ByteString
fieldValue')
                forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> (UUID -> DynamicValue
UUIDValue forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a. FromField a => FieldParser a
PG.fromField Field
field Maybe ByteString
fieldValue')
                forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> (Double -> DynamicValue
DoubleValue forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a. FromField a => FieldParser a
PG.fromField Field
field Maybe ByteString
fieldValue')
                forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> (UTCTime -> DynamicValue
DateTimeValue forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a. FromField a => FieldParser a
PG.fromField Field
field Maybe ByteString
fieldValue')
                forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> (Point -> DynamicValue
PointValue forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a. FromField a => FieldParser a
PG.fromField Field
field Maybe ByteString
fieldValue')
                forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> (forall a. FromField a => FieldParser a
PG.fromField @PG.Null Field
field Maybe ByteString
fieldValue' forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> forall (f :: * -> *) a. Applicative f => a -> f a
pure DynamicValue
IHP.DataSync.DynamicQuery.Null)
                forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> forall {f :: * -> *} {a} {p}.
(Applicative f, ConvertibleStrings a Text) =>
p -> Maybe a -> f DynamicValue
fromFieldCustomEnum Field
field Maybe ByteString
fieldValue'

            fromFieldCustomEnum :: p -> Maybe a -> f DynamicValue
fromFieldCustomEnum p
field (Just a
value) = forall (f :: * -> *) a. Applicative f => a -> f a
pure (Text -> DynamicValue
TextValue (forall a b. ConvertibleStrings a b => a -> b
cs a
value))
            fromFieldCustomEnum p
field Maybe a
Nothing      = forall (f :: * -> *) a. Applicative f => a -> f a
pure DynamicValue
IHP.DataSync.DynamicQuery.Null

instance PG.FromField UndecodedJSON where
    fromField :: FieldParser UndecodedJSON
fromField Field
field (Just ByteString
value) = forall (f :: * -> *) a. Applicative f => a -> f a
pure (ByteString -> UndecodedJSON
UndecodedJSON ByteString
value)
    fromField Field
field Maybe ByteString
Nothing = forall (f :: * -> *) a. Applicative f => a -> f a
pure (ByteString -> UndecodedJSON
UndecodedJSON ByteString
"null")

-- | Returns a list of all id's in a result
recordIds :: [[Field]] -> [UUID]
recordIds :: [[Field]] -> [UUID]
recordIds [[Field]]
result = [[Field]]
result
        forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat
        forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> forall a. (a -> Bool) -> [a] -> [a]
filter (\Field { Text
fieldName :: Text
$sel:fieldName:Field :: Field -> Text
fieldName } -> Text
fieldName forall a. Eq a => a -> a -> Bool
== Text
"id")
        forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> forall a b. (a -> b) -> [a] -> [b]
map (forall model (name :: Symbol) value.
(KnownSymbol name, HasField name model value) =>
Proxy name -> model -> value
get forall a. IsLabel "fieldValue" a => a
#fieldValue)
        forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe \case
            UUIDValue UUID
uuid -> forall a. a -> Maybe a
Just UUID
uuid
            DynamicValue
otherwise      -> forall a. Maybe a
Nothing



-- Here you can add functions which are available in all your controllers

-- | Transforms the keys of a JSON object from field name to column name
--
-- >>> transformColumnNamesToFieldNames [json|{"isCompleted": true}|]
-- [json|{"is_completed": true}|]
transformColumnNamesToFieldNames :: Value -> Value
transformColumnNamesToFieldNames :: Value -> Value
transformColumnNamesToFieldNames (Object Object
hashMap) =
        Object
hashMap
        forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> forall v. KeyMap v -> [(Key, v)]
Aeson.toList
        forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> forall a b. (a -> b) -> [a] -> [b]
map (\(Key
key, Value
value) -> ((Text -> Text) -> Key -> Key
applyKey Text -> Text
columnNameToFieldName Key
key, Value
value))
        forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> forall v. [(Key, v)] -> KeyMap v
Aeson.fromList
        forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> Object -> Value
Object
    where
        applyKey :: (Text -> Text) -> Key -> Key
applyKey Text -> Text
function Key
key =
            Key
key
                forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> Key -> Text
Aeson.toText
                forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> Text -> Text
function
                forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> Text -> Key
Aeson.fromText


$(deriveFromJSON defaultOptions ''FunctionCall)
$(deriveFromJSON defaultOptions 'QueryBuilder.OrCondition)
$(deriveFromJSON defaultOptions 'QueryBuilder.Join)
$(deriveFromJSON defaultOptions ''QueryBuilder.OrderByDirection)
$(deriveFromJSON defaultOptions 'QueryBuilder.OrderByClause)
$(deriveFromJSON defaultOptions 'SelectAll)
$(deriveFromJSON defaultOptions ''ConditionOperator)
$(deriveFromJSON defaultOptions ''DynamicValue)
$(deriveFromJSON defaultOptions ''ConditionExpression)

instance FromJSON DynamicSQLQuery where
    parseJSON :: Value -> Parser DynamicSQLQuery
parseJSON = forall a. String -> (Object -> Parser a) -> Value -> Parser a
withObject String
"DynamicSQLQuery" forall a b. (a -> b) -> a -> b
$ \Object
v -> Text
-> SelectedColumns
-> Maybe ConditionExpression
-> [OrderByClause]
-> Maybe ByteString
-> Maybe Int
-> Maybe Int
-> DynamicSQLQuery
DynamicSQLQuery
        forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Object
v forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"table"
        forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
v forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"selectedColumns"
        forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
v forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"whereCondition"
        forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
v forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"orderByClause"
        forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
v forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"distinctOnColumn" -- distinctOnColumn can be absent in older versions of ihp-datasync.js
        forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
v forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"limit" -- Limit can be absent in older versions of ihp-datasync.js
        forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
v forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"offset" -- Offset can be absent in older versions of ihp-datasync.js


instance FromJSON OrderByClause where
    parseJSON :: Value -> Parser OrderByClause
parseJSON = forall a. String -> (Object -> Parser a) -> Value -> Parser a
withObject String
"OrderByClause" forall a b. (a -> b) -> a -> b
$ \Object
v -> do
        let oldFormat :: Parser OrderByClause
oldFormat = ByteString -> OrderByDirection -> OrderByClause
OrderByClause
                forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Object
v forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"orderByColumn"
                forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
v forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"orderByDirection"
        let tagged :: Parser OrderByClause
tagged = do
                Text
tag <- Object
v forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"tag"
                case Text
tag of
                    Text
"OrderByClause" -> Parser OrderByClause
oldFormat
                    Text
"OrderByTSRank" -> Text -> Text -> OrderByClause
OrderByTSRank forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Object
v forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"tsvector" forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
v forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"tsquery"
                    Text
otherwise -> forall a. Text -> a
error (Text
"Invalid tag: " forall a. Semigroup a => a -> a -> a
<> Text
otherwise)
        Parser OrderByClause
tagged forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Parser OrderByClause
oldFormat