156 lines
7.2 KiB
Haskell
156 lines
7.2 KiB
Haskell
{- This Source Code Form is subject to the terms of the Mozilla Public License,
|
|
v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
|
obtain one at https://mozilla.org/MPL/2.0/. -}
|
|
|
|
{-# LANGUAGE ExplicitForAll #-}
|
|
{-# LANGUAGE OverloadedStrings #-}
|
|
|
|
-- | This module provides a representation of a @GraphQL@ Schema in addition to
|
|
-- functions for defining and manipulating schemas.
|
|
module Language.GraphQL.Type.Schema
|
|
( schema
|
|
, module Language.GraphQL.Type.Internal
|
|
) where
|
|
|
|
import Data.HashMap.Strict (HashMap)
|
|
import qualified Data.HashMap.Strict as HashMap
|
|
import Language.GraphQL.AST.DirectiveLocation (DirectiveLocation(..))
|
|
import qualified Language.GraphQL.AST.DirectiveLocation as DirectiveLocation
|
|
import qualified Language.GraphQL.AST as Full
|
|
import Language.GraphQL.Type.Internal
|
|
( Directive(..)
|
|
, Directives
|
|
, Schema
|
|
, Type(..)
|
|
, directives
|
|
, mutation
|
|
, subscription
|
|
, query
|
|
, types
|
|
)
|
|
import qualified Language.GraphQL.Type.Definition as Definition
|
|
import qualified Language.GraphQL.Type.Internal as Internal
|
|
import qualified Language.GraphQL.Type.In as In
|
|
import qualified Language.GraphQL.Type.Out as Out
|
|
|
|
-- | Schema constructor.
|
|
schema :: forall m
|
|
. Out.ObjectType m -- ^ Query type.
|
|
-> Maybe (Out.ObjectType m) -- ^ Mutation type.
|
|
-> Maybe (Out.ObjectType m) -- ^ Subscription type.
|
|
-> Directives -- ^ Directive definitions.
|
|
-> Schema m -- ^ Schema.
|
|
schema queryRoot mutationRoot subscriptionRoot directiveDefinitions =
|
|
Internal.Schema queryRoot mutationRoot subscriptionRoot allDirectives collectedTypes
|
|
where
|
|
collectedTypes = collectReferencedTypes queryRoot mutationRoot subscriptionRoot
|
|
allDirectives = HashMap.union directiveDefinitions defaultDirectives
|
|
defaultDirectives = HashMap.fromList
|
|
[ ("skip", skipDirective)
|
|
, ("include", includeDirective)
|
|
, ("deprecated", deprecatedDirective)
|
|
]
|
|
includeDirective =
|
|
Directive includeDescription skipIncludeLocations includeArguments
|
|
includeArguments = HashMap.singleton "if"
|
|
$ In.Argument (Just "Included when true.") ifType Nothing
|
|
includeDescription = Just
|
|
"Directs the executor to include this field or fragment only when the \
|
|
\`if` argument is true."
|
|
skipDirective = Directive skipDescription skipIncludeLocations skipArguments
|
|
skipArguments = HashMap.singleton "if"
|
|
$ In.Argument (Just "skipped when true.") ifType Nothing
|
|
ifType = In.NonNullScalarType Definition.boolean
|
|
skipDescription = Just
|
|
"Directs the executor to skip this field or fragment when the `if` \
|
|
\argument is true."
|
|
skipIncludeLocations =
|
|
[ ExecutableDirectiveLocation DirectiveLocation.Field
|
|
, ExecutableDirectiveLocation DirectiveLocation.FragmentSpread
|
|
, ExecutableDirectiveLocation DirectiveLocation.InlineFragment
|
|
]
|
|
deprecatedDirective =
|
|
Directive deprecatedDescription deprecatedLocations deprecatedArguments
|
|
reasonDescription = Just
|
|
"Explains why this element was deprecated, usually also including a \
|
|
\suggestion for how to access supported similar data. Formatted using \
|
|
\the Markdown syntax, as specified by \
|
|
\[CommonMark](https://commonmark.org/).'"
|
|
deprecatedArguments = HashMap.singleton "reason"
|
|
$ In.Argument reasonDescription reasonType
|
|
$ Just "No longer supported"
|
|
reasonType = In.NamedScalarType Definition.string
|
|
deprecatedDescription = Just
|
|
"Marks an element of a GraphQL schema as no longer supported."
|
|
deprecatedLocations =
|
|
[ TypeSystemDirectiveLocation DirectiveLocation.FieldDefinition
|
|
, TypeSystemDirectiveLocation DirectiveLocation.ArgumentDefinition
|
|
, TypeSystemDirectiveLocation DirectiveLocation.InputFieldDefinition
|
|
, TypeSystemDirectiveLocation DirectiveLocation.EnumValue
|
|
]
|
|
|
|
-- | Traverses the schema and finds all referenced types.
|
|
collectReferencedTypes :: forall m
|
|
. Out.ObjectType m
|
|
-> Maybe (Out.ObjectType m)
|
|
-> Maybe (Out.ObjectType m)
|
|
-> HashMap Full.Name (Type m)
|
|
collectReferencedTypes queryRoot mutationRoot subscriptionRoot =
|
|
let queryTypes = traverseObjectType queryRoot HashMap.empty
|
|
mutationTypes = maybe queryTypes (`traverseObjectType` queryTypes)
|
|
mutationRoot
|
|
in maybe mutationTypes (`traverseObjectType` queryTypes) subscriptionRoot
|
|
where
|
|
collect traverser typeName element foundTypes
|
|
| HashMap.member typeName foundTypes = foundTypes
|
|
| otherwise = traverser $ HashMap.insert typeName element foundTypes
|
|
visitFields (Out.Field _ outputType arguments) foundTypes
|
|
= traverseOutputType outputType
|
|
$ foldr visitArguments foundTypes arguments
|
|
visitArguments (In.Argument _ inputType _) = traverseInputType inputType
|
|
visitInputFields (In.InputField _ inputType _) = traverseInputType inputType
|
|
getField (Out.ValueResolver field _) = field
|
|
getField (Out.EventStreamResolver field _ _) = field
|
|
traverseInputType (In.InputObjectBaseType objectType) =
|
|
let In.InputObjectType typeName _ inputFields = objectType
|
|
element = InputObjectType objectType
|
|
traverser = flip (foldr visitInputFields) inputFields
|
|
in collect traverser typeName element
|
|
traverseInputType (In.ListBaseType listType) =
|
|
traverseInputType listType
|
|
traverseInputType (In.ScalarBaseType scalarType) =
|
|
let Definition.ScalarType typeName _ = scalarType
|
|
in collect Prelude.id typeName (ScalarType scalarType)
|
|
traverseInputType (In.EnumBaseType enumType) =
|
|
let Definition.EnumType typeName _ _ = enumType
|
|
in collect Prelude.id typeName (EnumType enumType)
|
|
traverseOutputType (Out.ObjectBaseType objectType) =
|
|
traverseObjectType objectType
|
|
traverseOutputType (Out.InterfaceBaseType interfaceType) =
|
|
traverseInterfaceType interfaceType
|
|
traverseOutputType (Out.UnionBaseType unionType) =
|
|
let Out.UnionType typeName _ types' = unionType
|
|
traverser = flip (foldr traverseObjectType) types'
|
|
in collect traverser typeName (UnionType unionType)
|
|
traverseOutputType (Out.ListBaseType listType) =
|
|
traverseOutputType listType
|
|
traverseOutputType (Out.ScalarBaseType scalarType) =
|
|
let Definition.ScalarType typeName _ = scalarType
|
|
in collect Prelude.id typeName (ScalarType scalarType)
|
|
traverseOutputType (Out.EnumBaseType enumType) =
|
|
let Definition.EnumType typeName _ _ = enumType
|
|
in collect Prelude.id typeName (EnumType enumType)
|
|
traverseObjectType objectType foundTypes =
|
|
let Out.ObjectType typeName _ interfaces fields = objectType
|
|
element = ObjectType objectType
|
|
traverser = polymorphicTraverser interfaces (getField <$> fields)
|
|
in collect traverser typeName element foundTypes
|
|
traverseInterfaceType interfaceType foundTypes =
|
|
let Out.InterfaceType typeName _ interfaces fields = interfaceType
|
|
element = InterfaceType interfaceType
|
|
traverser = polymorphicTraverser interfaces fields
|
|
in collect traverser typeName element foundTypes
|
|
polymorphicTraverser interfaces fields
|
|
= flip (foldr visitFields) fields
|
|
. flip (foldr traverseInterfaceType) interfaces
|