{-# LANGUAGE DataKinds #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
module Data.Morpheus.Execution.Client.Build
( defineQuery
) where
import Data.Semigroup ((<>))
import Language.Haskell.TH
import Data.Morpheus.Error.Client.Client (renderGQLErrors)
import Data.Morpheus.Execution.Client.Aeson (deriveFromJSON, deriveToJSON)
import Data.Morpheus.Execution.Client.Compile (validateWith)
import Data.Morpheus.Execution.Client.Fetch (deriveFetch)
import Data.Morpheus.Execution.Internal.Declare (declareType)
import Data.Morpheus.Types.Internal.Data (DataTypeKind (..), DataTypeLib, isOutputObject)
import Data.Morpheus.Types.Internal.DataD (GQLTypeD (..), QueryD (..), TypeD (..))
import Data.Morpheus.Types.Internal.Validation (Validation)
import Data.Morpheus.Types.Types (GQLQueryRoot (..))
defineQuery :: IO (Validation DataTypeLib) -> (GQLQueryRoot, String) -> Q [Dec]
defineQuery ioSchema queryRoot = do
schema <- runIO ioSchema
case schema >>= (`validateWith` queryRoot) of
Left errors -> fail (renderGQLErrors errors)
Right queryD -> defineQueryD queryD
defineQueryD :: QueryD -> Q [Dec]
defineQueryD QueryD {queryTypes = rootType:subTypes, queryText, queryArgsType} = do
rootDecs <- defineOperationType (queryArgumentType queryArgsType) queryText rootType
subTypeDecs <- concat <$> traverse declareT subTypes
return $ rootDecs ++ subTypeDecs
where
declareT GQLTypeD {typeD, typeKindD}
| isOutputObject typeKindD || typeKindD == KindUnion = withToJSON declareOutputType typeD
| typeKindD == KindEnum = withToJSON declareInputType typeD
| otherwise = declareInputType typeD
defineQueryD QueryD {queryTypes = []} = return []
declareOutputType :: TypeD -> Q [Dec]
declareOutputType typeD = pure [declareType False Nothing [''Show] typeD]
declareInputType :: TypeD -> Q [Dec]
declareInputType typeD = do
toJSONDec <- deriveToJSON typeD
pure $ declareType True Nothing [''Show] typeD : toJSONDec
withToJSON :: (TypeD -> Q [Dec]) -> TypeD -> Q [Dec]
withToJSON f datatype = do
toJson <- deriveFromJSON datatype
dec <- f datatype
pure (toJson : dec)
queryArgumentType :: Maybe TypeD -> (Type, Q [Dec])
queryArgumentType Nothing = (ConT $ mkName "()", pure [])
queryArgumentType (Just rootType@TypeD {tName}) = (ConT $ mkName tName, declareInputType rootType)
defineOperationType :: (Type, Q [Dec]) -> String -> GQLTypeD -> Q [Dec]
defineOperationType (argType, argumentTypes) query GQLTypeD {typeD} = do
rootType <- withToJSON declareOutputType typeD
typeClassFetch <- deriveFetch argType (tName typeD) query
argsT <- argumentTypes
pure $ rootType <> typeClassFetch <> argsT