morpheus-graphql-0.6.0: src/Data/Morpheus/Rendering/RenderIntrospection.hs
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeOperators #-}
module Data.Morpheus.Rendering.RenderIntrospection
( render
, createObjectType
) where
import Control.Monad.Fail (MonadFail)
import Data.Semigroup ((<>))
import Data.Text (Text, unpack)
import Data.Morpheus.Schema.Schema
-- Morpheus
import Data.Morpheus.Schema.TypeKind (TypeKind (..))
import Data.Morpheus.Types.Internal.Data (DataField (..), DataObject, DataTyCon (..), DataType (..),
DataTypeKind (..), DataTypeLib, DataTypeWrapper (..), DataUnion,
TypeAlias (..), kindOf, lookupDataType, toGQLWrapper)
import Data.Morpheus.Types.Internal.Value (convertToJSONName)
constRes :: Applicative m => a -> b -> m a
constRes = const . pure
type Result m a = DataTypeLib -> m a
class RenderSchema a b where
render :: (Monad m, MonadFail m) => (Text, a) -> DataTypeLib -> m (b m)
instance RenderSchema DataType S__Type where
render (key, DataScalar DataTyCon {typeDescription}) =
constRes $ createLeafType SCALAR key typeDescription Nothing
render (key, DataEnum DataTyCon {typeDescription, typeData}) =
constRes $ createLeafType ENUM key typeDescription (Just $ map createEnumValue typeData)
render (name, DataInputObject iObject) = renderInputObject (name, iObject)
render (name, DataObject object') = typeFromObject (name, object')
where
typeFromObject (key, DataTyCon {typeData, typeDescription}) lib =
createObjectType key typeDescription <$>
(Just <$> traverse (`render` lib) (filter (not . fieldHidden . snd) typeData))
render (name, DataUnion union') = const $ pure $ typeFromUnion (name, union')
render (name, DataInputUnion inpUnion') = renderInputUnion (name, inpUnion')
instance RenderSchema DataField S__Field where
render (key, field@DataField {fieldType = TypeAlias {aliasTyCon}, fieldArgs}) lib = do
kind <- renderTypeKind <$> lookupKind aliasTyCon lib
createFieldWith key (wrap field $ createType kind aliasTyCon Nothing $ Just []) <$>
traverse (`inputValueFromArg` lib) fieldArgs
renderTypeKind :: DataTypeKind -> TypeKind
renderTypeKind KindScalar = SCALAR
renderTypeKind (KindObject _) = OBJECT
renderTypeKind KindUnion = UNION
renderTypeKind KindInputUnion = INPUT_OBJECT
renderTypeKind KindEnum = ENUM
renderTypeKind KindInputObject = INPUT_OBJECT
renderTypeKind KindList = LIST
renderTypeKind KindNonNull = NON_NULL
wrap :: Monad m => DataField -> S__Type m -> S__Type m
wrap DataField {fieldType = TypeAlias {aliasWrappers}} typ = foldr wrapByTypeWrapper typ (toGQLWrapper aliasWrappers)
wrapByTypeWrapper :: Monad m => DataTypeWrapper -> S__Type m -> S__Type m
wrapByTypeWrapper ListType = wrapAs LIST
wrapByTypeWrapper NonNullType = wrapAs NON_NULL
lookupKind :: (Monad m, MonadFail m) => Text -> Result m DataTypeKind
lookupKind name lib =
case lookupDataType name lib of
Nothing -> fail $ unpack ("Kind Not Found: " <> name)
Just value -> pure (kindOf value)
inputValueFromArg :: (Monad m,MonadFail m) => (Text, DataField) -> Result m (S__InputValue m)
inputValueFromArg (key, input) = fmap (createInputValueWith key) . createInputObjectType input
createInputObjectType :: (Monad m, MonadFail m) => DataField -> Result m (S__Type m)
createInputObjectType field@DataField {fieldType = TypeAlias {aliasTyCon}} lib = do
kind <- renderTypeKind <$> lookupKind aliasTyCon lib
pure $ wrap field $ createType kind aliasTyCon Nothing $ Just []
renderInputObject :: (Monad m, MonadFail m) => (Text, DataObject) -> Result m (S__Type m)
renderInputObject (key, DataTyCon {typeData, typeDescription}) lib = do
fields <- traverse (`inputValueFromArg` lib) typeData
pure $ createInputObject key typeDescription fields
renderInputUnion :: (Monad m, MonadFail m) => (Text, DataUnion) -> Result m (S__Type m)
renderInputUnion (key', DataTyCon {typeData, typeDescription}) lib =
createInputObject key' typeDescription <$> traverse createField typeData
where
createField field = createInputValueWith (fieldName field) <$> createInputObjectType field lib
createLeafType :: Monad m => TypeKind -> Text -> Maybe Text -> Maybe [S__EnumValue m] -> S__Type m
createLeafType kind name description enums =
S__Type
{ s__TypeKind = constRes kind
, s__TypeName = constRes $ Just name
, s__TypeDescription = constRes description
, s__TypeFields = constRes Nothing
, s__TypeOfType = constRes Nothing
, s__TypeInterfaces = constRes Nothing
, s__TypePossibleTypes = constRes Nothing
, s__TypeEnumValues = constRes enums
, s__TypeInputFields = constRes Nothing
}
typeFromUnion :: Monad m => (Text, DataUnion) -> S__Type m
typeFromUnion (name, DataTyCon {typeData, typeDescription}) =
S__Type
{ s__TypeKind = constRes UNION
, s__TypeName = constRes $ Just name
, s__TypeDescription = constRes typeDescription
, s__TypeFields = constRes Nothing
, s__TypeOfType = constRes Nothing
, s__TypeInterfaces = constRes Nothing
, s__TypePossibleTypes =
constRes $ Just (map (\x -> createObjectType (aliasTyCon $ fieldType x) Nothing $ Just []) typeData)
, s__TypeEnumValues = constRes Nothing
, s__TypeInputFields = constRes Nothing
}
createObjectType :: Monad m => Text -> Maybe Text -> Maybe [S__Field m] -> S__Type m
createObjectType name description fields =
S__Type
{ s__TypeKind = constRes OBJECT
, s__TypeName = constRes $ Just name
, s__TypeDescription = constRes description
, s__TypeFields = constRes fields
, s__TypeOfType = constRes Nothing
, s__TypeInterfaces = constRes $ Just []
, s__TypePossibleTypes = constRes Nothing
, s__TypeEnumValues = constRes Nothing
, s__TypeInputFields = constRes Nothing
}
createInputObject :: Monad m => Text -> Maybe Text -> [S__InputValue m] -> S__Type m
createInputObject name description fields =
S__Type
{ s__TypeKind = constRes INPUT_OBJECT
, s__TypeName = constRes $ Just name
, s__TypeDescription = constRes description
, s__TypeFields = constRes Nothing
, s__TypeOfType = constRes Nothing
, s__TypeInterfaces = constRes Nothing
, s__TypePossibleTypes = constRes Nothing
, s__TypeEnumValues = constRes Nothing
, s__TypeInputFields = constRes $ Just fields
}
createType :: Monad m => TypeKind -> Text -> Maybe Text -> Maybe [S__Field m] -> S__Type m
createType kind name description fields' =
S__Type
{ s__TypeKind = constRes kind
, s__TypeName = constRes $ Just name
, s__TypeDescription = constRes description
, s__TypeFields = constRes fields'
, s__TypeOfType = constRes Nothing
, s__TypeInterfaces = constRes Nothing
, s__TypePossibleTypes = constRes Nothing
, s__TypeEnumValues = constRes $ Just []
, s__TypeInputFields = constRes Nothing
}
wrapAs :: Monad m => TypeKind -> S__Type m -> S__Type m
wrapAs kind contentType =
S__Type
{ s__TypeKind = constRes kind
, s__TypeName = constRes Nothing
, s__TypeDescription = constRes Nothing
, s__TypeFields = constRes Nothing
, s__TypeOfType = constRes $ Just contentType
, s__TypeInterfaces = constRes Nothing
, s__TypePossibleTypes = constRes Nothing
, s__TypeEnumValues = constRes Nothing
, s__TypeInputFields = constRes Nothing
}
createFieldWith :: Monad m => Text -> S__Type m -> [S__InputValue m] -> S__Field m
createFieldWith _name fieldType fieldArgs =
S__Field
{ s__FieldName = constRes (convertToJSONName _name)
, s__FieldDescription = constRes Nothing
, s__FieldArgs = constRes fieldArgs
, s__FieldType' = constRes fieldType
, s__FieldIsDeprecated = constRes False
, s__FieldDeprecationReason = constRes Nothing
}
createInputValueWith :: Monad m => Text -> S__Type m -> S__InputValue m
createInputValueWith name ivType =
S__InputValue
{ s__InputValueName = constRes (convertToJSONName name)
, s__InputValueDescription = constRes Nothing
, s__InputValueType' = constRes ivType
, s__InputValueDefaultValue = constRes Nothing
}
createEnumValue :: Monad m => Text -> S__EnumValue m
createEnumValue name =
S__EnumValue
{ s__EnumValueName = constRes name
, s__EnumValueDescription = constRes Nothing
, s__EnumValueIsDeprecated = constRes False
, s__EnumValueDeprecationReason = constRes Nothing
}