{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE FlexibleContexts , FlexibleInstances #-}
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
( DataInputUnion
, FieldDefinition(..)
, TypeContent(..)
, TypeDefinition(..)
, DataTypeKind(..)
, Schema
, DataTypeWrapper(..)
, DataUnion
, Meta(..)
, TypeRef(..)
, createInputUnionFields
, fieldVisibility
, kindOf
, lookupDataType
, toGQLWrapper
, DataEnumValue(..)
, lookupDeprecated
, DataInputUnion
, lookupDeprecatedReason
, convertToJSONName
, ArgumentsDefinition(..)
)
import Data.Morpheus.Types.Internal.Operation
( Listable(..)
)
import Data.Morpheus.Types.Internal.Resolving
( Failure(..) )
constRes :: Applicative m => a -> b -> m a
constRes = const . pure
type Result m a = Schema -> m a
class RenderSchema a b where
render :: (Monad m, Failure Text m) => a -> Schema -> m (b m)
instance RenderSchema TypeDefinition S__Type where
render TypeDefinition { typeName , typeMeta, typeContent } = __render typeContent
where
__render
:: (Monad m, Failure Text m) => TypeContent -> Schema -> m (S__Type m)
__render DataScalar{} =
constRes $ createLeafType SCALAR typeName typeMeta Nothing
__render (DataEnum enums) = constRes
$ createLeafType ENUM typeName typeMeta (Just $ map createEnumValue enums)
__render (DataInputObject fields) = \lib ->
createInputObject typeName typeMeta
<$> traverse (`renderinputValue` lib) (toList fields)
__render DataObject {objectFields} = \lib ->
createObjectType typeName (typeMeta >>= metaDescription)
<$> (Just <$> traverse (`render` lib) (filter fieldVisibility $ toList objectFields))
__render (DataUnion union) =
constRes $ typeFromUnion (typeName, typeMeta, union)
__render (DataInputUnion members) =
renderInputUnion (typeName, typeMeta, members)
createEnumValue :: Monad m => DataEnumValue -> S__EnumValue m
createEnumValue DataEnumValue { enumName, enumMeta } = S__EnumValue
{ s__EnumValueName = pure enumName
, s__EnumValueDescription = pure (enumMeta >>= metaDescription)
, s__EnumValueIsDeprecated = pure (isJust deprecated)
, s__EnumValueDeprecationReason = pure (deprecated >>= lookupDeprecatedReason)
}
where deprecated = enumMeta >>= lookupDeprecated
renderArguments :: (Monad m, Failure Text m) => ArgumentsDefinition -> Schema -> m [S__InputValue m]
renderArguments ArgumentsDefinition { arguments} lib = traverse (`renderinputValue` lib) $ toList arguments
renderArguments NoArguments _ = pure []
instance RenderSchema FieldDefinition S__Field where
render field@FieldDefinition { fieldName ,fieldType = TypeRef { typeConName }, fieldArgs, fieldMeta } lib
= do
kind <- renderTypeKind <$> lookupKind typeConName lib
pure S__Field
{ s__FieldName = pure (convertToJSONName fieldName)
, s__FieldDescription = pure (fieldMeta >>= metaDescription)
, s__FieldArgs = renderArguments fieldArgs lib
, s__FieldType' =
pure (applyTypeWrapper field $ createType kind typeConName Nothing $ Just [])
, s__FieldIsDeprecated = pure (isJust deprecated)
, s__FieldDeprecationReason = pure
(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
applyTypeWrapper :: Monad m => FieldDefinition -> S__Type m -> S__Type m
applyTypeWrapper FieldDefinition { fieldType = TypeRef { typeWrappers } } typ =
foldr wrapByTypeWrapper typ (toGQLWrapper typeWrappers)
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)
=> FieldDefinition
-> Result m (S__InputValue m)
renderinputValue input = fmap (createInputValueWith (fieldName input) (fieldMeta input)) . createInputObjectType input
createInputObjectType
:: (Monad m, Failure Text m) => FieldDefinition -> Result m (S__Type m)
createInputObjectType field@FieldDefinition { fieldType = TypeRef { typeConName } } lib
= do
kind <- renderTypeKind <$> lookupKind typeConName lib
pure $ applyTypeWrapper field $ createType kind typeConName Nothing $ Just []
renderInputUnion
:: (Monad m, Failure Text m)
=> (Text, Maybe Meta, DataInputUnion)
-> Result m (S__Type m)
renderInputUnion (key, meta, fields) lib =
createInputObject key meta <$> traverse
createField
(createInputUnionFields key $ map fst $ filter snd fields)
where
createField field =
createInputValueWith (fieldName field) 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 = pure kind
, s__TypeName = pure $ Just name
, s__TypeDescription = pure (meta >>= metaDescription)
, s__TypeFields = constRes Nothing
, s__TypeOfType = pure Nothing
, s__TypeInterfaces = pure Nothing
, s__TypePossibleTypes = pure Nothing
, s__TypeEnumValues = constRes enums
, s__TypeInputFields = pure Nothing
}
typeFromUnion :: Monad m => (Text, Maybe Meta, DataUnion) -> S__Type m
typeFromUnion (name, typeMeta, typeContent) = S__Type
{ s__TypeKind = pure UNION
, s__TypeName = pure $ Just name
, s__TypeDescription = pure (typeMeta >>= metaDescription)
, s__TypeFields = constRes Nothing
, s__TypeOfType = pure Nothing
, s__TypeInterfaces = pure Nothing
, s__TypePossibleTypes =
pure $ Just (map (\x -> createObjectType x Nothing $ Just []) typeContent)
, s__TypeEnumValues = constRes Nothing
, s__TypeInputFields = pure Nothing
}
createObjectType
:: Monad m => Text -> Maybe Text -> Maybe [S__Field m] -> S__Type m
createObjectType name description fields = S__Type
{ s__TypeKind = pure OBJECT
, s__TypeName = pure $ Just name
, s__TypeDescription = pure description
, s__TypeFields = constRes fields
, s__TypeOfType = pure Nothing
, s__TypeInterfaces = pure $ Just []
, s__TypePossibleTypes = pure Nothing
, s__TypeEnumValues = constRes Nothing
, s__TypeInputFields = pure Nothing
}
createInputObject
:: Monad m => Text -> Maybe Meta -> [S__InputValue m] -> S__Type m
createInputObject name meta fields = S__Type
{ s__TypeKind = pure INPUT_OBJECT
, s__TypeName = pure $ Just name
, s__TypeDescription = pure (meta >>= metaDescription)
, s__TypeFields = constRes Nothing
, s__TypeOfType = pure Nothing
, s__TypeInterfaces = pure Nothing
, s__TypePossibleTypes = pure Nothing
, s__TypeEnumValues = constRes Nothing
, s__TypeInputFields = pure $ 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 = pure kind
, s__TypeName = pure $ Just name
, s__TypeDescription = pure description
, s__TypeFields = constRes fields
, s__TypeOfType = pure Nothing
, s__TypeInterfaces = pure Nothing
, s__TypePossibleTypes = pure Nothing
, s__TypeEnumValues = constRes $ Just []
, s__TypeInputFields = pure Nothing
}
wrapAs :: Monad m => TypeKind -> S__Type m -> S__Type m
wrapAs kind contentType = S__Type { s__TypeKind = pure kind
, s__TypeName = pure Nothing
, s__TypeDescription = pure Nothing
, s__TypeFields = constRes Nothing
, s__TypeOfType = pure $ Just contentType
, s__TypeInterfaces = pure Nothing
, s__TypePossibleTypes = pure Nothing
, s__TypeEnumValues = constRes Nothing
, s__TypeInputFields = pure Nothing
}
createInputValueWith
:: Monad m => Text -> Maybe Meta -> S__Type m -> S__InputValue m
createInputValueWith name meta ivType = S__InputValue
{ s__InputValueName = pure (convertToJSONName name)
, s__InputValueDescription = pure (meta >>= metaDescription)
, s__InputValueType' = pure ivType
, s__InputValueDefaultValue = pure Nothing
}