{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}
module Data.Morpheus.Client.Transform.Inputs
( renderNonOutputTypes,
renderOperationArguments,
)
where
import Control.Monad.Reader (asks)
import Data.Morpheus.Client.Transform.Core (Converter (..), getType, typeFrom)
import Data.Morpheus.Internal.Utils
( elems,
)
import Data.Morpheus.Types.Internal.AST
( ANY,
ArgumentsDefinition (..),
ConsD (..),
DataTypeKind (..),
FieldDefinition (..),
IN,
Operation (..),
RAW,
TRUE,
TypeContent (..),
TypeD (..),
TypeDefinition (..),
TypeName,
TypeRef (..),
VALID,
Variable (..),
VariableDefinitions,
getOperationName,
mkConsEnum,
removeDuplicates,
)
import Data.Morpheus.Types.Internal.Resolving
( resolveUpdates,
)
import Data.Semigroup ((<>))
renderArguments :: VariableDefinitions RAW -> TypeName -> Maybe TypeD
renderArguments variables argsName
| null variables = Nothing
| otherwise = Just rootArgumentsType
where
rootArgumentsType :: TypeD
rootArgumentsType =
TypeD
{ tName = argsName,
tNamespace = [],
tCons = [ConsD {cName = argsName, cFields = map fieldD (elems variables)}],
tMeta = Nothing,
tKind = KindInputObject
}
where
fieldD :: Variable RAW -> FieldDefinition ANY
fieldD Variable {variableName, variableType} =
FieldDefinition
{ fieldName = variableName,
fieldArgs = NoArguments,
fieldType = variableType,
fieldMeta = Nothing
}
renderOperationArguments :: Operation VALID -> Converter (Maybe TypeD)
renderOperationArguments Operation {operationName} = do
variables <- asks snd
pure $ renderArguments variables (getOperationName operationName <> "Args")
renderNonOutputTypes :: [TypeName] -> Converter [TypeD]
renderNonOutputTypes enums = do
variables <- elems <$> asks snd
inputTypeRequests <- resolveUpdates [] $ map (exploreInputTypeNames . typeConName . variableType) variables
concat <$> traverse buildInputType (removeDuplicates $ inputTypeRequests <> enums)
exploreInputTypeNames :: TypeName -> [TypeName] -> Converter [TypeName]
exploreInputTypeNames name collected
| name `elem` collected = pure collected
| otherwise = getType name >>= scanInpType
where
scanInpType TypeDefinition {typeContent, typeName} = scanType typeContent
where
scanType (DataInputObject fields) =
resolveUpdates
(name : collected)
(map toInputTypeD $ elems fields)
where
toInputTypeD :: FieldDefinition IN -> [TypeName] -> Converter [TypeName]
toInputTypeD FieldDefinition {fieldType = TypeRef {typeConName}} =
exploreInputTypeNames typeConName
scanType (DataEnum _) = pure (collected <> [typeName])
scanType _ = pure collected
buildInputType :: TypeName -> Converter [TypeD]
buildInputType name = getType name >>= generateTypes
where
generateTypes TypeDefinition {typeName, typeContent} = subTypes typeContent
where
subTypes :: TypeContent TRUE ANY -> Converter [TypeD]
subTypes (DataInputObject inputFields) = do
fields <- traverse toFieldD (elems inputFields)
pure
[ mkInputType
typeName
KindInputObject
[ ConsD
{ cName = typeName,
cFields = fields
}
]
]
subTypes (DataEnum enumTags) =
pure
[ mkInputType
typeName
KindEnum
(map mkConsEnum enumTags)
]
subTypes _ = pure []
mkInputType :: TypeName -> DataTypeKind -> [ConsD] -> TypeD
mkInputType tName tKind tCons =
TypeD
{ tName,
tNamespace = [],
tCons,
tKind,
tMeta = Nothing
}
toFieldD :: FieldDefinition cat -> Converter (FieldDefinition ANY)
toFieldD field@FieldDefinition {fieldType} = do
typeConName <- typeFrom [] <$> getType (typeConName fieldType)
pure $ field {fieldType = fieldType {typeConName}}