{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeOperators #-}
module Data.Morpheus.Rendering.RenderIntrospection
( render
, createObjectType
) where
import Data.Semigroup ((<>))
import Data.Text (Text, unpack)
import Data.Morpheus.Schema.Schema
import Data.Morpheus.Schema.TypeKind (TypeKind (..))
import Data.Morpheus.Types.Internal.Data (DataField (..), DataField, DataFullType (..), DataLeaf (..),
DataObject, DataTyCon (..), 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 => (Text, a) -> DataTypeLib -> m (b m)
instance RenderSchema DataFullType S__Type where
render (name, Leaf leaf) = render (name, leaf)
render (name, InputObject iObject) = renderInputObject (name, iObject)
render (name, OutputObject 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, Union union') = const $ pure $ typeFromUnion (name, union')
render (name, InputUnion inpUnion') = renderInputUnion (name, inpUnion')
instance RenderSchema DataLeaf S__Type where
render (key, BaseScalar DataTyCon {typeDescription}) _ = pure $ createLeafType SCALAR key typeDescription Nothing
render (key, CustomScalar DataTyCon {typeDescription}) _ = pure $ createLeafType SCALAR key typeDescription Nothing
render (key, LeafEnum DataTyCon {typeDescription, typeData}) _ =
pure $ createLeafType ENUM key typeDescription (Just $ map createEnumValue typeData)
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 => 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 => (Text, DataField) -> Result m (S__InputValue m)
inputValueFromArg (key, input) = fmap (createInputValueWith key) . createInputObjectType input
createInputObjectType :: Monad 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 => (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 => (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 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
}