diff options
Diffstat (limited to 'school/node_modules/graphql/validation/rules/VariablesInAllowedPositionRule.js.flow')
-rw-r--r-- | school/node_modules/graphql/validation/rules/VariablesInAllowedPositionRule.js.flow | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/school/node_modules/graphql/validation/rules/VariablesInAllowedPositionRule.js.flow b/school/node_modules/graphql/validation/rules/VariablesInAllowedPositionRule.js.flow new file mode 100644 index 0000000..722ee99 --- /dev/null +++ b/school/node_modules/graphql/validation/rules/VariablesInAllowedPositionRule.js.flow @@ -0,0 +1,98 @@ +// @flow strict +import inspect from '../../jsutils/inspect'; + +import { GraphQLError } from '../../error/GraphQLError'; + +import { Kind } from '../../language/kinds'; +import type { ValueNode } from '../../language/ast'; +import type { ASTVisitor } from '../../language/visitor'; + +import type { GraphQLSchema } from '../../type/schema'; +import type { GraphQLType } from '../../type/definition'; +import { isNonNullType } from '../../type/definition'; + +import { typeFromAST } from '../../utilities/typeFromAST'; +import { isTypeSubTypeOf } from '../../utilities/typeComparators'; + +import type { ValidationContext } from '../ValidationContext'; + +/** + * Variables passed to field arguments conform to type + */ +export function VariablesInAllowedPositionRule( + context: ValidationContext, +): ASTVisitor { + let varDefMap = Object.create(null); + + return { + OperationDefinition: { + enter() { + varDefMap = Object.create(null); + }, + leave(operation) { + const usages = context.getRecursiveVariableUsages(operation); + + for (const { node, type, defaultValue } of usages) { + const varName = node.name.value; + const varDef = varDefMap[varName]; + if (varDef && type) { + // A var type is allowed if it is the same or more strict (e.g. is + // a subtype of) than the expected type. It can be more strict if + // the variable type is non-null when the expected type is nullable. + // If both are list types, the variable item type can be more strict + // than the expected item type (contravariant). + const schema = context.getSchema(); + const varType = typeFromAST(schema, varDef.type); + if ( + varType && + !allowedVariableUsage( + schema, + varType, + varDef.defaultValue, + type, + defaultValue, + ) + ) { + const varTypeStr = inspect(varType); + const typeStr = inspect(type); + context.reportError( + new GraphQLError( + `Variable "$${varName}" of type "${varTypeStr}" used in position expecting type "${typeStr}".`, + [varDef, node], + ), + ); + } + } + } + }, + }, + VariableDefinition(node) { + varDefMap[node.variable.name.value] = node; + }, + }; +} + +/** + * Returns true if the variable is allowed in the location it was found, + * which includes considering if default values exist for either the variable + * or the location at which it is located. + */ +function allowedVariableUsage( + schema: GraphQLSchema, + varType: GraphQLType, + varDefaultValue: ?ValueNode, + locationType: GraphQLType, + locationDefaultValue: ?mixed, +): boolean { + if (isNonNullType(locationType) && !isNonNullType(varType)) { + const hasNonNullVariableDefaultValue = + varDefaultValue != null && varDefaultValue.kind !== Kind.NULL; + const hasLocationDefaultValue = locationDefaultValue !== undefined; + if (!hasNonNullVariableDefaultValue && !hasLocationDefaultValue) { + return false; + } + const nullableLocationType = locationType.ofType; + return isTypeSubTypeOf(schema, varType, nullableLocationType); + } + return isTypeSubTypeOf(schema, varType, locationType); +} |