graphql-engine-1.0.0: GraphQL API over Postgres
Safe HaskellNone
LanguageHaskell2010

Hasura.GraphQL.Schema.Remote

Synopsis

Documentation

newtype Altered Source #

Helper, used to track whether an input value was altered during its parsing. There are two possible sources of alteration: - preset values, and - type name customizations. They might force evaluation of variables, and encapsulation of sub-JSON expressions as new variables. Each parser indicates whether such alteration took place within its part of the tree. See Note [Variable expansion in remote schema input parsers] for more information.

Constructors

Altered 

Fields

inputValueDefinitionParser :: forall r m n. MonadBuildRemoteSchema r m n => RemoteSchemaIntrospection -> InputValueDefinition -> m (InputFieldsParser n (Maybe (Altered, Value RemoteSchemaVariable))) Source #

inputValueDefinitionParser accepts a InputValueDefinition and will return an InputFieldsParser for it. If a non Input GraphQL type is found in the 'type' of the InputValueDefinition then an error will be thrown.

Each parser also returns a boolean that indicates whether the parsed value was altered by presets. Presets might force the evaluation of variables that would otherwise be transmitted unmodified.

remoteFieldScalarParser :: MonadParse n => MkTypename -> ScalarTypeDefinition -> Parser 'Both n (Altered, Value RemoteSchemaVariable) Source #

remoteFieldScalarParser attempts to parse a scalar value for a given remote field

We do not attempt to verify that the literal is correct! Some GraphQL implementations, including ours, are a bit flexible with the intepretations of literals; for instance, there are several places in our schema where we declare something to be an Int, but actually accept String literals. We do however peform variable type-checking.

If we encounter a JSON value, it means that we were introspecting a query variable. To call the remote schema, we need a graphql value; we therefore need to treat that JSON expression as if it were a query variable of its own. To avoid ending up with one such variable per scalar in the query, we also track alterations, to apply optimizations. See Note [Variable expansion in remote schema input parsers] for more information.

If the value contains a variable with a customized type name then we need to consider it to be altered to ensure that the original type name is passed to the remote server.

remoteInputObjectParser :: forall r m n. MonadBuildRemoteSchema r m n => RemoteSchemaIntrospection -> InputObjectTypeDefinition RemoteSchemaInputValueDefinition -> m (Either (InputFieldsParser n (Altered, Value RemoteSchemaVariable)) (Parser 'Input n (Altered, Value RemoteSchemaVariable))) Source #

remoteInputObjectParser returns an input parser for a given InputObjectTypeDefinition

Now, this is tricky! We are faced with two contradicting constraints here. On one hand, the GraphQL spec forbids us from creating empty input objects. This means that if all the arguments have presets, we CANNOT use the parser this function creates, and the caller cannot create a field for this object (and instead should use pure to include the preset values in the result of parsing the fields).

One way we could fix this would be to change the type of this function to return a `Maybe Parser`, inspect the result of argumentsParser, and return Nothing when we realize that there aren't any actual field in it (or at least return a value that propagates the preset values). But this would contradict our second constraint: this function needs to be memoized!

At time of writing, we can't memoize functions that return arbitrary functors of Parsers; so no memoizing Maybe Parser or Either Presets Parser. Which means that we would need to first call argumentsParser, then memoize the Just branch that builds the actual Parser. The problem is that the recursive call ro remoteSchemaInputObject is within argumentsParser, meaning the call to it MUST be in the memoized branch!

This is why, in the end, we do the following: we first test whether there is any non-preset field: if yes, we memoize that branch and proceed as normal. Otherwise we can omit the memoization: we know for sure that the preset fields won't generate a recursive call!

shortCircuitIfUnaltered :: forall k n. ('Input <: k, MonadParse n) => Parser k n (Maybe (Altered, Value RemoteSchemaVariable)) -> Parser k n (Maybe (Altered, Value RemoteSchemaVariable)) Source #

Variable expansion optimization. Since each parser returns a value that indicates whether it was altered, we can detect when no alteration took place, and replace the parsed and expanded value by its original. See Note [Variable expansion in remote schema input parsers] for more information.

argumentsParser :: forall r m n. MonadBuildRemoteSchema r m n => ArgumentsDefinition RemoteSchemaInputValueDefinition -> RemoteSchemaIntrospection -> m (InputFieldsParser n (Altered, HashMap Name (Value RemoteSchemaVariable))) Source #

argumentsParser is used for creating an argument parser for remote fields, This function is called for field arguments and input object fields. This function works in the following way:

For example, consider the following input objects:

input MessageWhereInpObj { id: IntCompareObj name: StringCompareObj }

input IntCompareObj { eq : Int @preset(value: 2) gt : Int lt : Int }

parsing a MessageWhereInpObj will result in the following call tree:

  • > argumentsParser MessageWhereInpObj
  • > id => inputValueDefinitionParser IntCompareObj
  • > remoteInputObjectParser IntCompareObj
  • > argumentsParser IntCompareObj
  • > eq => using preset, no recursion
  • > gt => inputValueDefinitionParser Int
  • > remoteFieldScalarParser Int
  • > lt => inputValueDefinitionParser Int
  • > remoteFieldScalarParser Int
  • > name => inputValueDefinitionParser StringCompareObj
  • > ...

Furthermore, like all other input parsers in this file, argumentsParser indicates whether this part of the tree was altered during parsing; if any of the fields is preset, or recursively contains values that contain presets further down, then this result is labelled as altered.

remoteSchemaInterface :: forall r m n. MonadBuildRemoteSchema r m n => RemoteSchemaIntrospection -> RemoteSchemaRelationships -> InterfaceTypeDefinition [Name] RemoteSchemaInputValueDefinition -> m (Parser 'Output n (DeduplicatedSelectionSet (RemoteRelationshipField UnpreparedValue) RemoteSchemaVariable)) Source #

remoteSchemaInterface returns a output parser for a given InterfaceTypeDefinition. Also check Note [Querying remote schema interfaces]

remoteFieldFromName :: forall r m n. MonadBuildRemoteSchema r m n => RemoteSchemaIntrospection -> RemoteSchemaRelationships -> Name -> Name -> Maybe Description -> Name -> ArgumentsDefinition RemoteSchemaInputValueDefinition -> m (FieldParser n (GraphQLField (RemoteRelationshipField UnpreparedValue) RemoteSchemaVariable)) Source #

remoteFieldFromName accepts a GraphQL name and searches for its definition in the RemoteSchemaIntrospection.

remoteField :: forall r m n. MonadBuildRemoteSchema r m n => RemoteSchemaIntrospection -> RemoteSchemaRelationships -> Name -> Name -> Maybe Description -> ArgumentsDefinition RemoteSchemaInputValueDefinition -> TypeDefinition [Name] RemoteSchemaInputValueDefinition -> m (FieldParser n (GraphQLField (RemoteRelationshipField UnpreparedValue) RemoteSchemaVariable)) Source #

remoteField accepts a TypeDefinition and will returns a FieldParser for it. Note that the TypeDefinition should be of the GraphQL Output kind, when an GraphQL Input kind is provided, then error will be thrown.

getObjectParser :: forall r m n. MonadBuildRemoteSchema r m n => RemoteSchemaIntrospection -> RemoteSchemaRelationships -> (Name -> m (ObjectTypeDefinition RemoteSchemaInputValueDefinition)) -> Name -> m (Parser 'Output n (Name, ObjectSelectionSet (RemoteRelationshipField UnpreparedValue) RemoteSchemaVariable)) Source #

helper function to get a parser of an object with it's name This function is called from remoteSchemaInterface and remoteSchemaObject functions. Both of these have a slightly different implementation of getObject, which is the reason getObject is an argument to this function