Safe Haskell | None |
---|---|
Language | Haskell2010 |
Tools to analyze the structure of a GraphQL request.
Synopsis
- data Structure = Structure {
- _stSelection :: HashMap Name FieldInfo
- _stVariables :: HashMap Name VariableInfo
- data FieldInfo
- = FieldObjectInfo GType ObjectInfo
- | FieldScalarInfo GType ScalarInfo
- | FieldEnumInfo GType EnumInfo
- data ScalarInfo = ScalarInfo {
- _siTypeDefinition :: ScalarTypeDefinition
- data EnumInfo = EnumInfo {
- _eiTypeDefinition :: EnumTypeDefinition
- data ObjectInfo = ObjectInfo {
- _oiTypeDefinition :: ObjectTypeDefinition InputValueDefinition
- _oiSelection :: HashMap Name FieldInfo
- data VariableInfo = VariableInfo {
- _viType :: GType
- _viTypeInfo :: InputFieldInfo
- _viDefaultValue :: Maybe (Value Void)
- data InputFieldInfo
- data InputObjectInfo = InputObjectInfo {
- _ioiTypeDefinition :: InputObjectTypeDefinition InputValueDefinition
- _ioiFields :: ~(HashMap Name (GType, InputFieldInfo))
- diagnoseGraphQLQuery :: SchemaIntrospection -> TypedOperationDefinition NoFragments Name -> Maybe [Text]
- analyzeGraphQLQuery :: SchemaIntrospection -> TypedOperationDefinition NoFragments Name -> (Maybe Structure, [Text])
- analyzeObjectSelectionSet :: ObjectTypeDefinition InputValueDefinition -> SelectionSet NoFragments Name -> Analysis (HashMap Name FieldInfo)
- analyzeField :: GType -> TypeDefinition [Name] InputValueDefinition -> Field NoFragments Name -> Analysis (Maybe FieldInfo)
- analyzeVariables :: [VariableDefinition] -> Analysis (HashMap Name VariableInfo)
- analyzeInputField :: Name -> TypeDefinition [Name] InputValueDefinition -> CircularT Name InputFieldInfo Analysis InputFieldInfo
- newtype Analysis a = Analysis (ExceptT AnalysisError (ReaderT (Path, SchemaIntrospection) (Writer [AnalysisError])) a)
- runAnalysis :: SchemaIntrospection -> Analysis a -> (Maybe a, [Text])
- lookupType :: MonadReader (Path, SchemaIntrospection) m => Name -> m (Maybe (TypeDefinition [Name] InputValueDefinition))
- withField :: MonadReader (Path, SchemaIntrospection) m => Name -> m a -> m a
- throwDiagnosis :: (MonadReader (Path, SchemaIntrospection) m, MonadError AnalysisError m) => Diagnosis -> m a
- withCatchAndRecord :: (MonadReader (Path, SchemaIntrospection) m, MonadWriter [AnalysisError] m, MonadError AnalysisError m) => m a -> m (Maybe a)
- data AnalysisError = AnalysisError {
- _aePath :: Path
- _aeDiagnosis :: Diagnosis
- type Path = Seq Text
- data Diagnosis
- = RootTypeNotAnObject
- | TypeNotFound Name
- | EnumSelectionSet Name
- | ScalarSelectionSet Name
- | InputObjectInOutput Name
- | UnionInInput Name
- | ObjectInInput Name
- | InterfaceInInput Name
- | ObjectFieldNotFound Name Name
- | ObjectMissingSelectionSet Name
- | MismatchedFields Name GType GType
- render :: AnalysisError -> Text
- queryRootName :: Name
- mutationRootName :: Name
- subscriptionRootName :: Name
- typenameField :: FieldDefinition InputValueDefinition
- schemaField :: FieldDefinition InputValueDefinition
- typeField :: FieldDefinition InputValueDefinition
- mkReservedField :: Name -> Name -> FieldDefinition InputValueDefinition
Documentation
Overall structure of a given query. We extract the tree of fields in the output, and the graph of input variables.
Structure | |
|
Information about the type of an output field; whether the base type is an
object or a scalar, we store the correspoding GType
to keep track of the
modifiers applied to it (list or non-nullability).
FieldObjectInfo GType ObjectInfo | |
FieldScalarInfo GType ScalarInfo | |
FieldEnumInfo GType EnumInfo |
data ScalarInfo Source #
ScalarInfo | |
|
EnumInfo | |
|
data ObjectInfo Source #
ObjectInfo | |
|
data VariableInfo Source #
Information about a single variable of the query.
VariableInfo | |
|
data InputFieldInfo Source #
Information about the type of an input field; whether the base type is an
object or a scalar, we store the correspoding GType
to keep track of the
modifiers applied to it (list or non-nullability).
data InputObjectInfo Source #
InputObjectInfo | |
|
diagnoseGraphQLQuery :: SchemaIntrospection -> TypedOperationDefinition NoFragments Name -> Maybe [Text] Source #
Given the schema's definition, and a query, validate that the query is consistent. We do this by running the analysis, but discarding the result: we do not care about the structure, only about the validity of the query.
Returns Nothing
if the query is valid, or a list of messages otherwise.
analyzeGraphQLQuery :: SchemaIntrospection -> TypedOperationDefinition NoFragments Name -> (Maybe Structure, [Text]) Source #
Given the schema's definition, and a query, run the analysis.
We process all possible fields, and return a partially filled structure if necessary. Given the following query:
query { foo { bar } does_not_exist { ghsdflgh } }
We would return a structure containing:
foo: { bar: { } }
AND an error about "does_not_exist" not existing.
In some cases, however, we might not be able to produce a structure at all,
in which case we return Nothing
. This either indicates that something was
fundamentally wrong with the structure of the query (such as not finding an
object at the top level), or that a recoverable error was not caught properly
(see withCatchAndRecord
).
analyzeObjectSelectionSet :: ObjectTypeDefinition InputValueDefinition -> SelectionSet NoFragments Name -> Analysis (HashMap Name FieldInfo) Source #
Analyze the fields of an object selection set against its definition, and
emit the corresponding Selection
. We ignore the fields that fail, and we
continue accumulating the others.
analyzeField :: GType -> TypeDefinition [Name] InputValueDefinition -> Field NoFragments Name -> Analysis (Maybe FieldInfo) Source #
Analyze a given field, and attempt to build a corresponding FieldInfo
.
analyzeVariables :: [VariableDefinition] -> Analysis (HashMap Name VariableInfo) Source #
Analyzes the variables in the given query. This builds the graph of input
types associated with the variable. This process is, like any GraphQL schema
operation, inherently self-recursive, and we use CircularT
(a lesser
SchemaT
) to tie the knot.
analyzeInputField :: Name -> TypeDefinition [Name] InputValueDefinition -> CircularT Name InputFieldInfo Analysis InputFieldInfo Source #
Builds an InputFieldInfo
for a given typename.
This function is "memoized" using withCircular
to prevent processing the
same type more than once in case the input types are self-recursive.
The monad in which we run our analysis.
Has three capabilities: - reader carries the current path, and the full schema for lookups - writer logs all errors we have caught - except allows for short-circuiting errors
Analysis (ExceptT AnalysisError (ReaderT (Path, SchemaIntrospection) (Writer [AnalysisError])) a) |
runAnalysis :: SchemaIntrospection -> Analysis a -> (Maybe a, [Text]) Source #
lookupType :: MonadReader (Path, SchemaIntrospection) m => Name -> m (Maybe (TypeDefinition [Name] InputValueDefinition)) Source #
Look up a type in the schema.
withField :: MonadReader (Path, SchemaIntrospection) m => Name -> m a -> m a Source #
Add the current field to the error path.
throwDiagnosis :: (MonadReader (Path, SchemaIntrospection) m, MonadError AnalysisError m) => Diagnosis -> m a Source #
Throws an AnalysisError
by combining the given diagnosis with the current
path. This interrupts the computation in the given branch, and must be caught
for the analysis to resume.
withCatchAndRecord :: (MonadReader (Path, SchemaIntrospection) m, MonadWriter [AnalysisError] m, MonadError AnalysisError m) => m a -> m (Maybe a) Source #
Runs the given computation. if it fails, cacthes the error, records it in
the monad, and return Nothing
. This allows for a clean recovery.
data AnalysisError Source #
RootTypeNotAnObject | |
TypeNotFound Name | |
EnumSelectionSet Name | |
ScalarSelectionSet Name | |
InputObjectInOutput Name | |
UnionInInput Name | |
ObjectInInput Name | |
InterfaceInInput Name | |
ObjectFieldNotFound Name Name | |
ObjectMissingSelectionSet Name | |
MismatchedFields Name GType GType |
render :: AnalysisError -> Text Source #
queryRootName :: Name Source #
mutationRootName :: Name Source #
subscriptionRootName :: Name Source #
typenameField :: FieldDefinition InputValueDefinition Source #
schemaField :: FieldDefinition InputValueDefinition Source #
mkReservedField :: Name -> Name -> FieldDefinition InputValueDefinition Source #