{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE FlexibleContexts #-}
module Data.Morpheus.Rendering.RenderIntrospection
( render
, createObjectType
)
where
import Data.Semigroup ( (<>) )
import Data.Text ( Text )
import Data.Maybe ( isJust )
import Data.Morpheus.Schema.Schema
import Data.Morpheus.Schema.TypeKind ( TypeKind(..) )
import Data.Morpheus.Types.Internal.AST
( DataField(..)
, DataTyCon(..)
, DataType(..)
, DataTypeKind(..)
, DataTypeLib
, DataTypeWrapper(..)
, DataUnion
, Meta(..)
, TypeAlias(..)
, createInputUnionFields
, fieldVisibility
, kindOf
, lookupDataType
, toGQLWrapper
, DataEnumValue(..)
, lookupDeprecated
, lookupDeprecatedReason
, convertToJSONName )
import Data.Morpheus.Types.Internal.Resolving
( Failure(..) )
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, Failure Text m) => (Text, a) -> DataTypeLib -> m (b m)
instance RenderSchema DataType S__Type where
render (key, DataScalar DataTyCon { typeMeta }) =
constRes $ createLeafType SCALAR key typeMeta Nothing
render (key, DataEnum DataTyCon { typeMeta, typeData }) =
constRes
$ createLeafType ENUM key typeMeta (Just $ map createEnumValue typeData)
render (name, DataInputObject DataTyCon { typeData, typeMeta }) =
renderInputObject
where
renderInputObject lib = do
fields <- traverse (`renderinputValue` lib) typeData
pure $ createInputObject name typeMeta fields
render (name, DataObject object') = typeFromObject (name, object')
where
typeFromObject (key, DataTyCon { typeData, typeMeta }) lib =
createObjectType key (typeMeta >>= metaDescription)
<$> (Just <$> traverse (`render` lib) (filter fieldVisibility typeData))
render (name, DataUnion union) = constRes $ typeFromUnion (name, union)
render (name, DataInputUnion inpUnion') = renderInputUnion (name, inpUnion')
createEnumValue :: Monad m => DataEnumValue -> S__EnumValue m
createEnumValue DataEnumValue { enumName, enumMeta } = S__EnumValue
{ s__EnumValueName = constRes enumName
, s__EnumValueDescription = constRes (enumMeta >>= metaDescription)
, s__EnumValueIsDeprecated = constRes (isJust deprecated)
, s__EnumValueDeprecationReason = constRes
(deprecated >>= lookupDeprecatedReason)
}
where deprecated = enumMeta >>= lookupDeprecated
instance RenderSchema DataField S__Field where
render (name, field@DataField { fieldType = TypeAlias { aliasTyCon }, fieldArgs, fieldMeta }) lib
= do
kind <- renderTypeKind <$> lookupKind aliasTyCon lib
args <- traverse (`renderinputValue` lib) fieldArgs
pure S__Field
{ s__FieldName = constRes (convertToJSONName name)
, s__FieldDescription = constRes (fieldMeta >>= metaDescription)
, s__FieldArgs = constRes args
, s__FieldType' =
constRes (wrap field $ createType kind aliasTyCon Nothing $ Just [])
, s__FieldIsDeprecated = constRes (isJust deprecated)
, s__FieldDeprecationReason = constRes
(deprecated >>= lookupDeprecatedReason)
}
where deprecated = fieldMeta >>= lookupDeprecated
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, Failure Text m) => Text -> Result m DataTypeKind
lookupKind name lib = case lookupDataType name lib of
Nothing -> failure $ "Kind Not Found: " <> name
Just value -> pure (kindOf value)
renderinputValue
:: (Monad m, Failure Text m)
=> (Text, DataField)
-> Result m (S__InputValue m)
renderinputValue (key, input) =
fmap (createInputValueWith key (fieldMeta input))
. createInputObjectType input
createInputObjectType
:: (Monad m, Failure Text 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 []
renderInputUnion
:: (Monad m, Failure Text m) => (Text, DataUnion) -> Result m (S__Type m)
renderInputUnion (key, DataTyCon { typeData, typeMeta }) lib =
createInputObject key typeMeta
<$> traverse createField (createInputUnionFields key typeData)
where
createField (name, field) =
createInputValueWith name Nothing <$> createInputObjectType field lib
createLeafType
:: Monad m
=> TypeKind
-> Text
-> Maybe Meta
-> Maybe [S__EnumValue m]
-> S__Type m
createLeafType kind name meta enums = S__Type
{ s__TypeKind = constRes kind
, s__TypeName = constRes $ Just name
, s__TypeDescription = constRes (meta >>= metaDescription)
, 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, typeMeta }) = S__Type
{ s__TypeKind = constRes UNION
, s__TypeName = constRes $ Just name
, s__TypeDescription = constRes (typeMeta >>= metaDescription)
, s__TypeFields = constRes Nothing
, s__TypeOfType = constRes Nothing
, s__TypeInterfaces = constRes Nothing
, s__TypePossibleTypes =
constRes $ Just (map (\x -> createObjectType 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 Meta -> [S__InputValue m] -> S__Type m
createInputObject name meta fields = S__Type
{ s__TypeKind = constRes INPUT_OBJECT
, s__TypeName = constRes $ Just name
, s__TypeDescription = constRes (meta >>= metaDescription)
, 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
}
createInputValueWith
:: Monad m => Text -> Maybe Meta -> S__Type m -> S__InputValue m
createInputValueWith name meta ivType = S__InputValue
{ s__InputValueName = constRes (convertToJSONName name)
, s__InputValueDescription = constRes (meta >>= metaDescription)
, s__InputValueType' = constRes ivType
, s__InputValueDefaultValue = constRes Nothing
}