Safe Haskell | None |
---|---|
Language | Haskell2010 |
Transform GraphQL query documents from AST into valid structures
This corresponds roughly to the Validation section of the specification, except where noted.
One core difference is that this module doesn't attempt to do any type-level validation, as we attempt to defer all of that to the Haskell type checker.
Deliberately not going to do:
- field selections on compound types https://facebook.github.io/graphql/#sec-Field-Selections-on-Objects-Interfaces-and-Unions-Types
- leaf field selections https://facebook.github.io/graphql/#sec-Leaf-Field-Selections
- argument names https://facebook.github.io/graphql/#sec-Argument-Names
- argument value type correctness https://facebook.github.io/graphql/#sec-Argument-Values-Type-Correctness
- fragment spread type existence https://facebook.github.io/graphql/#sec-Fragment-Spread-Type-Existence
- fragments on compound types https://facebook.github.io/graphql/#sec-Fragments-On-Composite-Types
- fragment spread is possible https://facebook.github.io/graphql/#sec-Fragment-spread-is-possible
- directives are defined https://facebook.github.io/graphql/#sec-Directives-Are-Defined
- directives are in valid locations https://facebook.github.io/graphql/#sec-Directives-Are-In-Valid-Locations
- variable default values are correctly typed https://facebook.github.io/graphql/#sec-Variable-Default-Values-Are-Correctly-Typed
- variables are input types https://facebook.github.io/graphql/#sec-Variables-Are-Input-Types
- all variable usages are allowed https://facebook.github.io/graphql/#sec-All-Variable-Usages-are-Allowed
Because all of the above rely on type checking.
- data ValidationError
- = DuplicateOperation Name
- | MixedAnonymousOperations Int [Name]
- | DuplicateArgument Name
- | DuplicateFragmentDefinition Name
- | NoSuchFragment Name
- | DuplicateDirective Name
- | DuplicateVariableDefinition Variable
- | CircularFragmentSpread Name
- | UnusedFragments (Set Name)
- | UnusedVariables (Set Variable)
- | UndefinedVariable Variable
- | InvalidValue Value
- | InvalidDefaultValue Value
- | MismatchedNames Name Name
- | MismatchedArguments Name
- | IncompatibleFields Name
- | TypeConditionNotFound Name
- type ValidationErrors = NonEmpty ValidationError
- data QueryDocument value
- = LoneAnonymousOperation (Operation value)
- | MultipleOperations (Operations value)
- validate :: Schema -> QueryDocument -> Either (NonEmpty ValidationError) (QueryDocument VariableValue)
- getErrors :: Schema -> QueryDocument -> [ValidationError]
- data Operation value
- getSelectionSet :: Operation value -> SelectionSetByType value
- data VariableDefinition = VariableDefinition {}
- type VariableValue = Value' (Either VariableDefinition ConstScalar)
- data Variable
- data Type
- data SelectionSetByType value
- newtype SelectionSet value = SelectionSet (OrderedMap ResponseKey (Field value))
- getSelectionSetForType :: Eq value => ObjectTypeDefinition -> SelectionSetByType value -> Either ValidationErrors (SelectionSet value)
- data Field value
- lookupArgument :: Field value -> Name -> Maybe value
- getSubSelectionSet :: Field value -> Maybe (SelectionSetByType value)
- type ResponseKey = Name
- getResponseKey :: Field' spread value -> ResponseKey
- findDuplicates :: Ord a => [a] -> [a]
Documentation
data ValidationError Source #
Errors arising from validating a document.
DuplicateOperation Name |
https://facebook.github.io/graphql/#sec-Operation-Name-Uniqueness |
MixedAnonymousOperations Int [Name] |
https://facebook.github.io/graphql/#sec-Lone-Anonymous-Operation |
DuplicateArgument Name |
|
DuplicateFragmentDefinition Name |
|
NoSuchFragment Name |
|
DuplicateDirective Name |
https://facebook.github.io/graphql/#sec-Directives-Are-Unique-Per-Location |
DuplicateVariableDefinition Variable | There were multiple variables defined with the same name. |
CircularFragmentSpread Name |
|
UnusedFragments (Set Name) |
|
UnusedVariables (Set Variable) | Variables were defined without being used. https://facebook.github.io/graphql/#sec-All-Variables-Used |
UndefinedVariable Variable | A variable was used without being defined. https://facebook.github.io/graphql/#sec-All-Variable-Uses-Defined |
InvalidValue Value | Value in AST wasn't valid. |
InvalidDefaultValue Value | Default value in AST contained variables. |
MismatchedNames Name Name | Two different names given for the same response key. |
MismatchedArguments Name | Two different sets of arguments given for the same response key. |
IncompatibleFields Name | Two fields had the same response key, one was a leaf, the other was not. |
TypeConditionNotFound Name | There's a type condition that's not present in the schema. |
data QueryDocument value Source #
A valid query document.
Construct this using validate
on an QueryDocument
.
LoneAnonymousOperation (Operation value) | The query document contains a single anonymous operation. |
MultipleOperations (Operations value) | The query document contains multiple uniquely-named operations. |
Eq value => Eq (QueryDocument value) Source # | |
Show value => Show (QueryDocument value) Source # | |
validate :: Schema -> QueryDocument -> Either (NonEmpty ValidationError) (QueryDocument VariableValue) Source #
Turn a parsed document into a known valid one.
The document is known to be syntactically valid, as we've got its AST. Here, we confirm that it's semantically valid (modulo types).
getErrors :: Schema -> QueryDocument -> [ValidationError] Source #
Identify all of the validation errors in doc
.
An empty list means no errors.
Operating on validated documents
getSelectionSet :: Operation value -> SelectionSetByType value Source #
Get the selection set for an operation.
Executing validated documents
data VariableDefinition Source #
Defines a variable within the context of an operation.
See https://facebook.github.io/graphql/#sec-Language.Variables
VariableDefinition | |
|
type VariableValue = Value' (Either VariableDefinition ConstScalar) Source #
A GraphQL value which might contain some defined variables.
Resolving queries
data SelectionSetByType value Source #
Functor SelectionSetByType Source # | |
Foldable SelectionSetByType Source # | |
Traversable SelectionSetByType Source # | |
Eq value => Eq (SelectionSetByType value) Source # | |
Ord value => Ord (SelectionSetByType value) Source # | |
Show value => Show (SelectionSetByType value) Source # | |
newtype SelectionSet value Source #
A selection set, almost fully validated.
Sub-selection sets might not be validated.
SelectionSet (OrderedMap ResponseKey (Field value)) |
Eq value => Eq (SelectionSet value) Source # | |
Ord value => Ord (SelectionSet value) Source # | |
Show value => Show (SelectionSet value) Source # | |
getSelectionSetForType Source #
:: Eq value | |
=> ObjectTypeDefinition | The type of the object that the selection set is for |
-> SelectionSetByType value | A selection set with type conditions, obtained from the validation process |
-> Either ValidationErrors (SelectionSet value) | A flattened selection set without type conditions. It's possible that some of the fields in various types are not mergeable, in which case, we'll return a validation error. |
Once we know the GraphQL type of the object that a selection set (i.e. a
SelectionSetByType
) is for, we can eliminate all the irrelevant types and
present a single, flattened map of ResponseKey
to Field
.
A field ready to be resolved.
lookupArgument :: Field value -> Name -> Maybe value Source #
Get the value of an argument in a field.
getSubSelectionSet :: Field value -> Maybe (SelectionSetByType value) Source #
Get the selection set within a field.
type ResponseKey = Name Source #
A ResponseKey
is the key under which a field appears in a response. If
there's an alias, it's the alias, if not, it's the field name.
getResponseKey :: Field' spread value -> ResponseKey Source #
Get the response key of a field.
"A field’s response key is its alias if an alias is provided, and it is otherwise the field’s name."
Exported for testing
findDuplicates :: Ord a => [a] -> [a] Source #
Return a list of all the elements with duplicates. The list of duplicates itself will not contain duplicates.
\xs -> findDuplicates @Int xs == ordNub (findDuplicates @Int xs)