{-# OPTIONS_HADDOCK not-home #-}
module GraphQL.Internal.Schema
( GType(..)
, Builtin(..)
, TypeDefinition(..)
, Name
, ArgumentDefinition(..)
, EnumValueDefinition(..)
, EnumTypeDefinition(..)
, FieldDefinition(..)
, Interfaces
, InterfaceTypeDefinition(..)
, ObjectTypeDefinition(..)
, UnionTypeDefinition(..)
, ScalarTypeDefinition(..)
, InputType(..)
, InputTypeDefinition(..)
, InputObjectTypeDefinition(..)
, InputObjectFieldDefinition(..)
, AnnotatedType(..)
, ListType(..)
, NonNullType(..)
, DefinesTypes(..)
, doesFragmentTypeApply
, getInputTypeDefinition
, builtinFromName
, astAnnotationToSchemaAnnotation
, Schema
, makeSchema
, emptySchema
, lookupType
) where
import Protolude
import qualified Data.Map as Map
import qualified GraphQL.Internal.Syntax.AST as AST
import GraphQL.Value (Value)
import GraphQL.Internal.Name (HasName(..), Name)
newtype Schema = Schema (Map Name TypeDefinition) deriving (Eq, Ord, Show)
makeSchema :: ObjectTypeDefinition -> Schema
makeSchema = Schema . getDefinedTypes
emptySchema :: Schema
emptySchema = Schema (Map.empty :: (Map Name TypeDefinition))
lookupType :: Schema -> Name -> Maybe TypeDefinition
lookupType (Schema schema) name = Map.lookup name schema
class DefinesTypes t where
getDefinedTypes :: t -> Map Name TypeDefinition
data AnnotatedType t = TypeNamed t
| TypeList (ListType t)
| TypeNonNull (NonNullType t)
deriving (Eq, Ord, Show)
getAnnotatedType :: AnnotatedType t -> t
getAnnotatedType (TypeNamed t) = t
getAnnotatedType (TypeList (ListType t)) = getAnnotatedType t
getAnnotatedType (TypeNonNull (NonNullTypeNamed t)) = t
getAnnotatedType (TypeNonNull (NonNullTypeList (ListType t))) = getAnnotatedType t
instance HasName t => HasName (AnnotatedType t) where
getName = getName . getAnnotatedType
newtype ListType t = ListType (AnnotatedType t) deriving (Eq, Ord, Show)
data NonNullType t = NonNullTypeNamed t
| NonNullTypeList (ListType t)
deriving (Eq, Ord, Show)
data GType = DefinedType TypeDefinition | BuiltinType Builtin deriving (Eq, Ord, Show)
instance DefinesTypes GType where
getDefinedTypes (BuiltinType _) = mempty
getDefinedTypes (DefinedType t) = getDefinedTypes t
instance HasName GType where
getName (DefinedType x) = getName x
getName (BuiltinType x) = getName x
data TypeDefinition = TypeDefinitionObject ObjectTypeDefinition
| TypeDefinitionInterface InterfaceTypeDefinition
| TypeDefinitionUnion UnionTypeDefinition
| TypeDefinitionScalar ScalarTypeDefinition
| TypeDefinitionEnum EnumTypeDefinition
| TypeDefinitionInputObject InputObjectTypeDefinition
| TypeDefinitionTypeExtension TypeExtensionDefinition
deriving (Eq, Ord, Show)
instance HasName TypeDefinition where
getName (TypeDefinitionObject x) = getName x
getName (TypeDefinitionInterface x) = getName x
getName (TypeDefinitionUnion x) = getName x
getName (TypeDefinitionScalar x) = getName x
getName (TypeDefinitionEnum x) = getName x
getName (TypeDefinitionInputObject x) = getName x
getName (TypeDefinitionTypeExtension x) = getName x
instance DefinesTypes TypeDefinition where
getDefinedTypes defn =
case defn of
TypeDefinitionObject x -> getDefinedTypes x
TypeDefinitionInterface x -> getDefinedTypes x
TypeDefinitionUnion x -> getDefinedTypes x
TypeDefinitionScalar x -> getDefinedTypes x
TypeDefinitionEnum x -> getDefinedTypes x
TypeDefinitionInputObject _ -> mempty
TypeDefinitionTypeExtension _ ->
panic "TODO: we should remove the 'extend' behaviour entirely"
data ObjectTypeDefinition = ObjectTypeDefinition Name Interfaces (NonEmpty FieldDefinition)
deriving (Eq, Ord, Show)
instance HasName ObjectTypeDefinition where
getName (ObjectTypeDefinition name _ _) = name
instance DefinesTypes ObjectTypeDefinition where
getDefinedTypes obj@(ObjectTypeDefinition name interfaces fields) =
Map.singleton name (TypeDefinitionObject obj) <>
foldMap getDefinedTypes interfaces <>
foldMap getDefinedTypes fields
type Interfaces = [InterfaceTypeDefinition]
data FieldDefinition = FieldDefinition Name [ArgumentDefinition] (AnnotatedType GType)
deriving (Eq, Ord, Show)
instance HasName FieldDefinition where
getName (FieldDefinition name _ _) = name
instance DefinesTypes FieldDefinition where
getDefinedTypes (FieldDefinition _ args retVal) =
getDefinedTypes (getAnnotatedType retVal) <>
foldMap getDefinedTypes args
data ArgumentDefinition = ArgumentDefinition Name (AnnotatedType InputType) (Maybe DefaultValue)
deriving (Eq, Ord, Show)
instance HasName ArgumentDefinition where
getName (ArgumentDefinition name _ _) = name
instance DefinesTypes ArgumentDefinition where
getDefinedTypes (ArgumentDefinition _ annotatedType _) = getDefinedTypes $ getAnnotatedType annotatedType
data InterfaceTypeDefinition = InterfaceTypeDefinition Name (NonEmpty FieldDefinition)
deriving (Eq, Ord, Show)
instance HasName InterfaceTypeDefinition where
getName (InterfaceTypeDefinition name _) = name
instance DefinesTypes InterfaceTypeDefinition where
getDefinedTypes i@(InterfaceTypeDefinition name fields) = Map.singleton name (TypeDefinitionInterface i) <> foldMap getDefinedTypes fields
data UnionTypeDefinition = UnionTypeDefinition Name (NonEmpty ObjectTypeDefinition)
deriving (Eq, Ord, Show)
instance HasName UnionTypeDefinition where
getName (UnionTypeDefinition name _) = name
instance DefinesTypes UnionTypeDefinition where
getDefinedTypes defn@(UnionTypeDefinition name objs) =
Map.singleton name (TypeDefinitionUnion defn) <>
foldMap getDefinedTypes objs
newtype ScalarTypeDefinition = ScalarTypeDefinition Name
deriving (Eq, Ord, Show)
instance HasName ScalarTypeDefinition where
getName (ScalarTypeDefinition name) = name
instance DefinesTypes ScalarTypeDefinition where
getDefinedTypes defn = Map.singleton (getName defn) (TypeDefinitionScalar defn)
data Builtin
= GInt
| GBool
| GString
| GFloat
| GID deriving (Eq, Ord, Show)
instance HasName Builtin where
getName GInt = "Int"
getName GBool = "Boolean"
getName GString = "String"
getName GFloat = "Float"
getName GID = "ID"
data EnumTypeDefinition = EnumTypeDefinition Name [EnumValueDefinition]
deriving (Eq, Ord, Show)
instance HasName EnumTypeDefinition where
getName (EnumTypeDefinition name _) = name
instance DefinesTypes EnumTypeDefinition where
getDefinedTypes enum = Map.singleton (getName enum) (TypeDefinitionEnum enum)
newtype EnumValueDefinition = EnumValueDefinition Name
deriving (Eq, Ord, Show)
instance HasName EnumValueDefinition where
getName (EnumValueDefinition name) = name
data InputObjectTypeDefinition = InputObjectTypeDefinition Name (NonEmpty InputObjectFieldDefinition)
deriving (Eq, Ord, Show)
instance HasName InputObjectTypeDefinition where
getName (InputObjectTypeDefinition name _) = name
data InputObjectFieldDefinition = InputObjectFieldDefinition Name (AnnotatedType InputType) (Maybe DefaultValue)
deriving (Eq, Ord, Show)
instance HasName InputObjectFieldDefinition where
getName (InputObjectFieldDefinition name _ _) = name
newtype TypeExtensionDefinition = TypeExtensionDefinition ObjectTypeDefinition
deriving (Eq, Ord, Show)
instance HasName TypeExtensionDefinition where
getName (TypeExtensionDefinition obj) = getName obj
data InputType = DefinedInputType InputTypeDefinition | BuiltinInputType Builtin deriving (Eq, Ord, Show)
instance HasName InputType where
getName (DefinedInputType x) = getName x
getName (BuiltinInputType x) = getName x
instance DefinesTypes InputType where
getDefinedTypes inputType =
case inputType of
DefinedInputType typeDefinition -> getDefinedTypes typeDefinition
BuiltinInputType _ -> mempty
data InputTypeDefinition
= InputTypeDefinitionObject InputObjectTypeDefinition
| InputTypeDefinitionScalar ScalarTypeDefinition
| InputTypeDefinitionEnum EnumTypeDefinition
deriving (Eq, Ord, Show)
instance HasName InputTypeDefinition where
getName (InputTypeDefinitionObject x) = getName x
getName (InputTypeDefinitionScalar x) = getName x
getName (InputTypeDefinitionEnum x) = getName x
instance DefinesTypes InputTypeDefinition where
getDefinedTypes inputTypeDefinition =
case inputTypeDefinition of
InputTypeDefinitionObject typeDefinition -> getDefinedTypes (TypeDefinitionInputObject typeDefinition)
InputTypeDefinitionScalar typeDefinition -> getDefinedTypes (TypeDefinitionScalar typeDefinition)
InputTypeDefinitionEnum typeDefinition -> getDefinedTypes (TypeDefinitionEnum typeDefinition)
type DefaultValue = Value
doesFragmentTypeApply :: ObjectTypeDefinition -> TypeDefinition -> Bool
doesFragmentTypeApply objectType fragmentType =
case fragmentType of
TypeDefinitionObject obj -> obj == objectType
TypeDefinitionInterface interface -> objectType `implements` interface
TypeDefinitionUnion union -> objectType `branchOf` union
_ -> False
where
implements (ObjectTypeDefinition _ interfaces _) int = int `elem` interfaces
branchOf obj (UnionTypeDefinition _ branches) = obj `elem` branches
getInputTypeDefinition :: TypeDefinition -> Maybe InputTypeDefinition
getInputTypeDefinition td =
case td of
TypeDefinitionInputObject itd -> Just (InputTypeDefinitionObject itd)
TypeDefinitionScalar itd -> Just (InputTypeDefinitionScalar itd)
TypeDefinitionEnum itd -> Just (InputTypeDefinitionEnum itd)
_ -> Nothing
builtinFromName :: Name -> Maybe Builtin
builtinFromName typeName
| typeName == getName GInt = Just GInt
| typeName == getName GBool = Just GBool
| typeName == getName GString = Just GString
| typeName == getName GFloat = Just GFloat
| typeName == getName GID = Just GID
| otherwise = Nothing
astAnnotationToSchemaAnnotation :: AST.GType -> a -> AnnotatedType a
astAnnotationToSchemaAnnotation gtype schemaTypeName =
case gtype of
AST.TypeNamed _ -> TypeNamed schemaTypeName
AST.TypeList (AST.ListType astTypeName) -> TypeList (ListType $ astAnnotationToSchemaAnnotation astTypeName schemaTypeName)
AST.TypeNonNull (AST.NonNullTypeNamed _) -> TypeNonNull (NonNullTypeNamed schemaTypeName)
AST.TypeNonNull (AST.NonNullTypeList (AST.ListType astTypeName)) -> TypeNonNull (NonNullTypeList (ListType (astAnnotationToSchemaAnnotation astTypeName schemaTypeName)))