{-# LANGUAGE DataKinds #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
module Data.Morpheus.Execution.Client.Build
( defineQuery
) where
import Control.Lens (declareLenses)
import Data.Aeson (ToJSON)
import Data.Semigroup ((<>))
import Language.Haskell.TH
import Data.Morpheus.Error.Client.Client (renderGQLErrors)
import Data.Morpheus.Execution.Client.Aeson (deriveFromJSON)
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 (DataTypeLib)
import Data.Morpheus.Types.Internal.DataD (QueryD (..), TypeD (..))
import Data.Morpheus.Types.Internal.Validation (Validation)
import Data.Morpheus.Types.Types (GQLQueryRoot (..))
queryArgumentType :: [TypeD] -> (Type, Q [Dec])
queryArgumentType [] = (ConT $ mkName "()", pure [])
queryArgumentType (rootType@TypeD {tName}:xs) = (ConT $ mkName tName, types)
where
types = pure $ map (declareType [''Show, ''ToJSON]) (rootType : xs)
defineJSONType :: TypeD -> Q [Dec]
defineJSONType datatype = do
record <- declareLenses (pure [declareType [''Show] datatype])
toJson <- pure <$> deriveFromJSON datatype
pure $ record <> toJson
defineOperationType :: (Type, Q [Dec]) -> String -> TypeD -> Q [Dec]
defineOperationType (argType, argumentTypes) query datatype = do
rootType <- defineJSONType datatype
typeClassFetch <- deriveFetch argType (tName datatype) query
args <- argumentTypes
pure $ rootType <> typeClassFetch <> args
defineQueryD :: QueryD -> Q [Dec]
defineQueryD QueryD {queryTypes = rootType:subTypes, queryText, queryArgTypes} = do
rootDecs <- defineOperationType (queryArgumentType queryArgTypes) queryText rootType
subTypeDecs <- concat <$> mapM defineJSONType subTypes
return $ rootDecs ++ subTypeDecs
defineQueryD QueryD {queryTypes = []} = return []
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