diff --git a/src/utilities/isValidLiteralValue.js b/src/utilities/isValidLiteralValue.js index 6109218f11..719a2c24aa 100644 --- a/src/utilities/isValidLiteralValue.js +++ b/src/utilities/isValidLiteralValue.js @@ -15,7 +15,7 @@ import { visit, visitWithTypeInfo } from '../language/visitor'; import type { GraphQLInputType } from '../type/definition'; import { GraphQLSchema } from '../type/schema'; import { ValuesOfCorrectType } from '../validation/rules/ValuesOfCorrectType'; -import { ValidationContext } from '../validation/validate'; +import ValidationContext from '../validation/ValidationContext'; /** * Utility which determines if a value literal node is valid for an input type. diff --git a/src/validation/ValidationContext.js b/src/validation/ValidationContext.js new file mode 100644 index 0000000000..98f2e21cc4 --- /dev/null +++ b/src/validation/ValidationContext.js @@ -0,0 +1,221 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict + */ + +import type { ObjMap } from '../jsutils/ObjMap'; +import { GraphQLError } from '../error'; +import { visit, visitWithTypeInfo } from '../language/visitor'; +import { Kind } from '../language/kinds'; +import type { + DocumentNode, + OperationDefinitionNode, + VariableNode, + SelectionSetNode, + FragmentSpreadNode, + FragmentDefinitionNode, +} from '../language/ast'; +import { GraphQLSchema } from '../type/schema'; +import type { + GraphQLInputType, + GraphQLOutputType, + GraphQLCompositeType, + GraphQLField, + GraphQLArgument, +} from '../type/definition'; +import type { GraphQLDirective } from '../type/directives'; +import { TypeInfo } from '../utilities/TypeInfo'; + +type NodeWithSelectionSet = OperationDefinitionNode | FragmentDefinitionNode; +type VariableUsage = { node: VariableNode, type: ?GraphQLInputType }; + +/** + * An instance of this class is passed as the "this" context to all validators, + * allowing access to commonly useful contextual information from within a + * validation rule. + */ +export default class ValidationContext { + _schema: GraphQLSchema; + _ast: DocumentNode; + _typeInfo: TypeInfo; + _errors: Array; + _fragments: ObjMap; + _fragmentSpreads: Map>; + _recursivelyReferencedFragments: Map< + OperationDefinitionNode, + $ReadOnlyArray, + >; + _variableUsages: Map>; + _recursiveVariableUsages: Map< + OperationDefinitionNode, + $ReadOnlyArray, + >; + + constructor( + schema: GraphQLSchema, + ast: DocumentNode, + typeInfo: TypeInfo, + ): void { + this._schema = schema; + this._ast = ast; + this._typeInfo = typeInfo; + this._errors = []; + this._fragmentSpreads = new Map(); + this._recursivelyReferencedFragments = new Map(); + this._variableUsages = new Map(); + this._recursiveVariableUsages = new Map(); + } + + reportError(error: GraphQLError): void { + this._errors.push(error); + } + + getErrors(): $ReadOnlyArray { + return this._errors; + } + + getSchema(): GraphQLSchema { + return this._schema; + } + + getDocument(): DocumentNode { + return this._ast; + } + + getFragment(name: string): ?FragmentDefinitionNode { + let fragments = this._fragments; + if (!fragments) { + this._fragments = fragments = this.getDocument().definitions.reduce( + (frags, statement) => { + if (statement.kind === Kind.FRAGMENT_DEFINITION) { + frags[statement.name.value] = statement; + } + return frags; + }, + Object.create(null), + ); + } + return fragments[name]; + } + + getFragmentSpreads( + node: SelectionSetNode, + ): $ReadOnlyArray { + let spreads = this._fragmentSpreads.get(node); + if (!spreads) { + spreads = []; + const setsToVisit: Array = [node]; + while (setsToVisit.length !== 0) { + const set = setsToVisit.pop(); + for (let i = 0; i < set.selections.length; i++) { + const selection = set.selections[i]; + if (selection.kind === Kind.FRAGMENT_SPREAD) { + spreads.push(selection); + } else if (selection.selectionSet) { + setsToVisit.push(selection.selectionSet); + } + } + } + this._fragmentSpreads.set(node, spreads); + } + return spreads; + } + + getRecursivelyReferencedFragments( + operation: OperationDefinitionNode, + ): $ReadOnlyArray { + let fragments = this._recursivelyReferencedFragments.get(operation); + if (!fragments) { + fragments = []; + const collectedNames = Object.create(null); + const nodesToVisit: Array = [operation.selectionSet]; + while (nodesToVisit.length !== 0) { + const node = nodesToVisit.pop(); + const spreads = this.getFragmentSpreads(node); + for (let i = 0; i < spreads.length; i++) { + const fragName = spreads[i].name.value; + if (collectedNames[fragName] !== true) { + collectedNames[fragName] = true; + const fragment = this.getFragment(fragName); + if (fragment) { + fragments.push(fragment); + nodesToVisit.push(fragment.selectionSet); + } + } + } + } + this._recursivelyReferencedFragments.set(operation, fragments); + } + return fragments; + } + + getVariableUsages(node: NodeWithSelectionSet): $ReadOnlyArray { + let usages = this._variableUsages.get(node); + if (!usages) { + const newUsages = []; + const typeInfo = new TypeInfo(this._schema); + visit( + node, + visitWithTypeInfo(typeInfo, { + VariableDefinition: () => false, + Variable(variable) { + newUsages.push({ node: variable, type: typeInfo.getInputType() }); + }, + }), + ); + usages = newUsages; + this._variableUsages.set(node, usages); + } + return usages; + } + + getRecursiveVariableUsages( + operation: OperationDefinitionNode, + ): $ReadOnlyArray { + let usages = this._recursiveVariableUsages.get(operation); + if (!usages) { + usages = this.getVariableUsages(operation); + const fragments = this.getRecursivelyReferencedFragments(operation); + for (let i = 0; i < fragments.length; i++) { + Array.prototype.push.apply( + usages, + this.getVariableUsages(fragments[i]), + ); + } + this._recursiveVariableUsages.set(operation, usages); + } + return usages; + } + + getType(): ?GraphQLOutputType { + return this._typeInfo.getType(); + } + + getParentType(): ?GraphQLCompositeType { + return this._typeInfo.getParentType(); + } + + getInputType(): ?GraphQLInputType { + return this._typeInfo.getInputType(); + } + + getParentInputType(): ?GraphQLInputType { + return this._typeInfo.getParentInputType(); + } + + getFieldDef(): ?GraphQLField<*, *> { + return this._typeInfo.getFieldDef(); + } + + getDirective(): ?GraphQLDirective { + return this._typeInfo.getDirective(); + } + + getArgument(): ?GraphQLArgument { + return this._typeInfo.getArgument(); + } +} diff --git a/src/validation/index.js b/src/validation/index.js index 01b7e47e2f..6d33dce405 100644 --- a/src/validation/index.js +++ b/src/validation/index.js @@ -7,7 +7,11 @@ * @flow strict */ -export { validate, ValidationContext } from './validate'; +export { validate } from './validate'; + +// https://github.com/tc39/proposal-export-default-from +import ValidationContext from './ValidationContext'; +export { ValidationContext }; export { specifiedRules } from './specifiedRules'; diff --git a/src/validation/rules/ExecutableDefinitions.js b/src/validation/rules/ExecutableDefinitions.js index 6f8c5ec6fa..b7b2d9a382 100644 --- a/src/validation/rules/ExecutableDefinitions.js +++ b/src/validation/rules/ExecutableDefinitions.js @@ -7,7 +7,7 @@ * @flow strict */ -import type { ValidationContext } from '../index'; +import type ValidationContext from '../ValidationContext'; import { GraphQLError } from '../../error'; import { Kind } from '../../language/kinds'; import type { ASTVisitor } from '../../language/visitor'; diff --git a/src/validation/rules/FieldsOnCorrectType.js b/src/validation/rules/FieldsOnCorrectType.js index c779a4270e..168fc1bca7 100644 --- a/src/validation/rules/FieldsOnCorrectType.js +++ b/src/validation/rules/FieldsOnCorrectType.js @@ -7,7 +7,7 @@ * @flow strict */ -import type { ValidationContext } from '../index'; +import type ValidationContext from '../ValidationContext'; import { GraphQLError } from '../../error'; import suggestionList from '../../jsutils/suggestionList'; import quotedOrList from '../../jsutils/quotedOrList'; diff --git a/src/validation/rules/FragmentsOnCompositeTypes.js b/src/validation/rules/FragmentsOnCompositeTypes.js index 83378629e4..ea03734d85 100644 --- a/src/validation/rules/FragmentsOnCompositeTypes.js +++ b/src/validation/rules/FragmentsOnCompositeTypes.js @@ -7,7 +7,7 @@ * @flow strict */ -import type { ValidationContext } from '../index'; +import type ValidationContext from '../ValidationContext'; import { GraphQLError } from '../../error'; import { print } from '../../language/printer'; import type { ASTVisitor } from '../../language/visitor'; diff --git a/src/validation/rules/KnownArgumentNames.js b/src/validation/rules/KnownArgumentNames.js index b2b77f4834..a775880c21 100644 --- a/src/validation/rules/KnownArgumentNames.js +++ b/src/validation/rules/KnownArgumentNames.js @@ -7,7 +7,7 @@ * @flow strict */ -import type { ValidationContext } from '../index'; +import type ValidationContext from '../ValidationContext'; import { GraphQLError } from '../../error'; import type { ASTVisitor } from '../../language/visitor'; import suggestionList from '../../jsutils/suggestionList'; diff --git a/src/validation/rules/KnownDirectives.js b/src/validation/rules/KnownDirectives.js index 61ce36e48a..9972ce8acf 100644 --- a/src/validation/rules/KnownDirectives.js +++ b/src/validation/rules/KnownDirectives.js @@ -7,7 +7,7 @@ * @flow strict */ -import type { ValidationContext } from '../index'; +import type ValidationContext from '../ValidationContext'; import { GraphQLError } from '../../error'; import find from '../../jsutils/find'; import { Kind } from '../../language/kinds'; diff --git a/src/validation/rules/KnownFragmentNames.js b/src/validation/rules/KnownFragmentNames.js index 33db878324..c3893415f0 100644 --- a/src/validation/rules/KnownFragmentNames.js +++ b/src/validation/rules/KnownFragmentNames.js @@ -7,7 +7,7 @@ * @flow strict */ -import type { ValidationContext } from '../index'; +import type ValidationContext from '../ValidationContext'; import { GraphQLError } from '../../error'; import type { ASTVisitor } from '../../language/visitor'; diff --git a/src/validation/rules/KnownTypeNames.js b/src/validation/rules/KnownTypeNames.js index 6da7c2d0e7..8ad1b7775f 100644 --- a/src/validation/rules/KnownTypeNames.js +++ b/src/validation/rules/KnownTypeNames.js @@ -7,7 +7,7 @@ * @flow strict */ -import type { ValidationContext } from '../index'; +import type ValidationContext from '../ValidationContext'; import { GraphQLError } from '../../error'; import suggestionList from '../../jsutils/suggestionList'; import quotedOrList from '../../jsutils/quotedOrList'; diff --git a/src/validation/rules/LoneAnonymousOperation.js b/src/validation/rules/LoneAnonymousOperation.js index 9dbe41a8a4..745de201cb 100644 --- a/src/validation/rules/LoneAnonymousOperation.js +++ b/src/validation/rules/LoneAnonymousOperation.js @@ -7,7 +7,7 @@ * @flow strict */ -import type { ValidationContext } from '../index'; +import type ValidationContext from '../ValidationContext'; import { GraphQLError } from '../../error'; import { Kind } from '../../language/kinds'; import type { ASTVisitor } from '../../language/visitor'; diff --git a/src/validation/rules/NoFragmentCycles.js b/src/validation/rules/NoFragmentCycles.js index 2c7891aa3a..0a51122759 100644 --- a/src/validation/rules/NoFragmentCycles.js +++ b/src/validation/rules/NoFragmentCycles.js @@ -7,7 +7,7 @@ * @flow strict */ -import type { ValidationContext } from '../index'; +import type ValidationContext from '../ValidationContext'; import { GraphQLError } from '../../error'; import type { FragmentDefinitionNode } from '../../language/ast'; import type { ASTVisitor } from '../../language/visitor'; diff --git a/src/validation/rules/NoUndefinedVariables.js b/src/validation/rules/NoUndefinedVariables.js index 0fdcac4a01..d74a49265b 100644 --- a/src/validation/rules/NoUndefinedVariables.js +++ b/src/validation/rules/NoUndefinedVariables.js @@ -7,7 +7,7 @@ * @flow strict */ -import type { ValidationContext } from '../index'; +import type ValidationContext from '../ValidationContext'; import { GraphQLError } from '../../error'; import type { ASTVisitor } from '../../language/visitor'; diff --git a/src/validation/rules/NoUnusedFragments.js b/src/validation/rules/NoUnusedFragments.js index 5a0902e772..4e2b6e2b7f 100644 --- a/src/validation/rules/NoUnusedFragments.js +++ b/src/validation/rules/NoUnusedFragments.js @@ -7,7 +7,7 @@ * @flow strict */ -import type { ValidationContext } from '../index'; +import type ValidationContext from '../ValidationContext'; import { GraphQLError } from '../../error'; import type { ASTVisitor } from '../../language/visitor'; diff --git a/src/validation/rules/NoUnusedVariables.js b/src/validation/rules/NoUnusedVariables.js index 505abd04a6..c5637e22db 100644 --- a/src/validation/rules/NoUnusedVariables.js +++ b/src/validation/rules/NoUnusedVariables.js @@ -7,7 +7,7 @@ * @flow strict */ -import type { ValidationContext } from '../index'; +import type ValidationContext from '../ValidationContext'; import { GraphQLError } from '../../error'; import type { ASTVisitor } from '../../language/visitor'; diff --git a/src/validation/rules/OverlappingFieldsCanBeMerged.js b/src/validation/rules/OverlappingFieldsCanBeMerged.js index b37f2f4156..6dea9df741 100644 --- a/src/validation/rules/OverlappingFieldsCanBeMerged.js +++ b/src/validation/rules/OverlappingFieldsCanBeMerged.js @@ -7,7 +7,7 @@ * @flow strict */ -import type { ValidationContext } from '../index'; +import type ValidationContext from '../ValidationContext'; import { GraphQLError } from '../../error'; import find from '../../jsutils/find'; import type { ObjMap } from '../../jsutils/ObjMap'; diff --git a/src/validation/rules/PossibleFragmentSpreads.js b/src/validation/rules/PossibleFragmentSpreads.js index 614b32c564..31bca238d8 100644 --- a/src/validation/rules/PossibleFragmentSpreads.js +++ b/src/validation/rules/PossibleFragmentSpreads.js @@ -7,7 +7,7 @@ * @flow strict */ -import type { ValidationContext } from '../index'; +import type ValidationContext from '../ValidationContext'; import { GraphQLError } from '../../error'; import type { ASTVisitor } from '../../language/visitor'; import { doTypesOverlap } from '../../utilities/typeComparators'; diff --git a/src/validation/rules/ProvidedNonNullArguments.js b/src/validation/rules/ProvidedNonNullArguments.js index cbefff5d90..5eda68eb32 100644 --- a/src/validation/rules/ProvidedNonNullArguments.js +++ b/src/validation/rules/ProvidedNonNullArguments.js @@ -7,7 +7,7 @@ * @flow strict */ -import type { ValidationContext } from '../index'; +import type ValidationContext from '../ValidationContext'; import { GraphQLError } from '../../error'; import keyMap from '../../jsutils/keyMap'; import { isNonNullType } from '../../type/definition'; diff --git a/src/validation/rules/ScalarLeafs.js b/src/validation/rules/ScalarLeafs.js index 22fa642a6a..8f2291c3c3 100644 --- a/src/validation/rules/ScalarLeafs.js +++ b/src/validation/rules/ScalarLeafs.js @@ -7,7 +7,7 @@ * @flow strict */ -import type { ValidationContext } from '../index'; +import type ValidationContext from '../ValidationContext'; import { GraphQLError } from '../../error'; import type { FieldNode } from '../../language/ast'; import { getNamedType, isLeafType } from '../../type/definition'; diff --git a/src/validation/rules/SingleFieldSubscriptions.js b/src/validation/rules/SingleFieldSubscriptions.js index af0650baaf..622e6fc5f8 100644 --- a/src/validation/rules/SingleFieldSubscriptions.js +++ b/src/validation/rules/SingleFieldSubscriptions.js @@ -7,7 +7,7 @@ * @flow strict */ -import type { ValidationContext } from '../index'; +import type ValidationContext from '../ValidationContext'; import { GraphQLError } from '../../error'; import type { OperationDefinitionNode } from '../../language/ast'; import type { ASTVisitor } from '../../language/visitor'; diff --git a/src/validation/rules/UniqueArgumentNames.js b/src/validation/rules/UniqueArgumentNames.js index 31c4af7099..0516401079 100644 --- a/src/validation/rules/UniqueArgumentNames.js +++ b/src/validation/rules/UniqueArgumentNames.js @@ -7,7 +7,7 @@ * @flow strict */ -import type { ValidationContext } from '../index'; +import type ValidationContext from '../ValidationContext'; import { GraphQLError } from '../../error'; import type { ASTVisitor } from '../../language/visitor'; diff --git a/src/validation/rules/UniqueDirectivesPerLocation.js b/src/validation/rules/UniqueDirectivesPerLocation.js index 3bc6fe18f4..fb143d2a15 100644 --- a/src/validation/rules/UniqueDirectivesPerLocation.js +++ b/src/validation/rules/UniqueDirectivesPerLocation.js @@ -7,7 +7,7 @@ * @flow strict */ -import type { ValidationContext } from '../index'; +import type ValidationContext from '../ValidationContext'; import { GraphQLError } from '../../error'; import type { DirectiveNode } from '../../language/ast'; import type { ASTVisitor } from '../../language/visitor'; diff --git a/src/validation/rules/UniqueFragmentNames.js b/src/validation/rules/UniqueFragmentNames.js index eab401ffb6..1a55cd7967 100644 --- a/src/validation/rules/UniqueFragmentNames.js +++ b/src/validation/rules/UniqueFragmentNames.js @@ -7,7 +7,7 @@ * @flow strict */ -import type { ValidationContext } from '../index'; +import type ValidationContext from '../ValidationContext'; import { GraphQLError } from '../../error'; import type { ASTVisitor } from '../../language/visitor'; diff --git a/src/validation/rules/UniqueInputFieldNames.js b/src/validation/rules/UniqueInputFieldNames.js index 96e39bea87..9290b60305 100644 --- a/src/validation/rules/UniqueInputFieldNames.js +++ b/src/validation/rules/UniqueInputFieldNames.js @@ -7,7 +7,7 @@ * @flow strict */ -import type { ValidationContext } from '../index'; +import type ValidationContext from '../ValidationContext'; import { GraphQLError } from '../../error'; import type { ASTVisitor } from '../../language/visitor'; diff --git a/src/validation/rules/UniqueOperationNames.js b/src/validation/rules/UniqueOperationNames.js index 04fc7addc1..765b0b80f4 100644 --- a/src/validation/rules/UniqueOperationNames.js +++ b/src/validation/rules/UniqueOperationNames.js @@ -7,7 +7,7 @@ * @flow strict */ -import type { ValidationContext } from '../index'; +import type ValidationContext from '../ValidationContext'; import { GraphQLError } from '../../error'; import type { ASTVisitor } from '../../language/visitor'; diff --git a/src/validation/rules/UniqueVariableNames.js b/src/validation/rules/UniqueVariableNames.js index a6d2e7cea8..90f84483ec 100644 --- a/src/validation/rules/UniqueVariableNames.js +++ b/src/validation/rules/UniqueVariableNames.js @@ -7,7 +7,7 @@ * @flow strict */ -import type { ValidationContext } from '../index'; +import type ValidationContext from '../ValidationContext'; import type { VariableDefinitionNode } from '../../language/ast'; import { GraphQLError } from '../../error'; import type { ASTVisitor } from '../../language/visitor'; diff --git a/src/validation/rules/ValuesOfCorrectType.js b/src/validation/rules/ValuesOfCorrectType.js index 532b670b8f..d440a4ec3e 100644 --- a/src/validation/rules/ValuesOfCorrectType.js +++ b/src/validation/rules/ValuesOfCorrectType.js @@ -7,7 +7,7 @@ * @flow strict */ -import type { ValidationContext } from '../index'; +import type ValidationContext from '../ValidationContext'; import { GraphQLError } from '../../error'; import type { ValueNode } from '../../language/ast'; import { print } from '../../language/printer'; diff --git a/src/validation/rules/VariablesAreInputTypes.js b/src/validation/rules/VariablesAreInputTypes.js index 74bbf663d7..b152025c02 100644 --- a/src/validation/rules/VariablesAreInputTypes.js +++ b/src/validation/rules/VariablesAreInputTypes.js @@ -7,7 +7,7 @@ * @flow strict */ -import type { ValidationContext } from '../index'; +import type ValidationContext from '../ValidationContext'; import { GraphQLError } from '../../error'; import type { VariableDefinitionNode } from '../../language/ast'; import { print } from '../../language/printer'; diff --git a/src/validation/rules/VariablesDefaultValueAllowed.js b/src/validation/rules/VariablesDefaultValueAllowed.js index 2b93963032..019b55e9fe 100644 --- a/src/validation/rules/VariablesDefaultValueAllowed.js +++ b/src/validation/rules/VariablesDefaultValueAllowed.js @@ -7,7 +7,7 @@ * @flow strict */ -import type { ValidationContext } from '../index'; +import type ValidationContext from '../ValidationContext'; import { GraphQLError } from '../../error'; import type { ASTVisitor } from '../../language/visitor'; import { isNonNullType } from '../../type/definition'; diff --git a/src/validation/rules/VariablesInAllowedPosition.js b/src/validation/rules/VariablesInAllowedPosition.js index e7214ed44e..47073e5757 100644 --- a/src/validation/rules/VariablesInAllowedPosition.js +++ b/src/validation/rules/VariablesInAllowedPosition.js @@ -7,7 +7,7 @@ * @flow strict */ -import type { ValidationContext } from '../index'; +import type ValidationContext from '../ValidationContext'; import { GraphQLError } from '../../error'; import type { ASTVisitor } from '../../language/visitor'; import { isNonNullType } from '../../type/definition'; diff --git a/src/validation/specifiedRules.js b/src/validation/specifiedRules.js index 48268e5dc6..b5e45f90cb 100644 --- a/src/validation/specifiedRules.js +++ b/src/validation/specifiedRules.js @@ -88,7 +88,7 @@ import { OverlappingFieldsCanBeMerged } from './rules/OverlappingFieldsCanBeMerg // Spec Section: "Input Object Field Uniqueness" import { UniqueInputFieldNames } from './rules/UniqueInputFieldNames'; -import type { ValidationContext } from './index'; +import type ValidationContext from './ValidationContext'; /** * This set includes all validation rules defined by the GraphQL spec. diff --git a/src/validation/validate.js b/src/validation/validate.js index 09a7494c8d..61213f8b86 100644 --- a/src/validation/validate.js +++ b/src/validation/validate.js @@ -8,31 +8,15 @@ */ import invariant from '../jsutils/invariant'; -import type { ObjMap } from '../jsutils/ObjMap'; import { GraphQLError } from '../error'; import { visit, visitInParallel, visitWithTypeInfo } from '../language/visitor'; -import { Kind } from '../language/kinds'; -import type { - DocumentNode, - OperationDefinitionNode, - VariableNode, - SelectionSetNode, - FragmentSpreadNode, - FragmentDefinitionNode, -} from '../language/ast'; +import type { DocumentNode } from '../language/ast'; import type { ASTVisitor } from '../language/visitor'; import { GraphQLSchema } from '../type/schema'; -import type { - GraphQLInputType, - GraphQLOutputType, - GraphQLCompositeType, - GraphQLField, - GraphQLArgument, -} from '../type/definition'; -import type { GraphQLDirective } from '../type/directives'; import { assertValidSchema } from '../type/validate'; import { TypeInfo } from '../utilities/TypeInfo'; import { specifiedRules } from './specifiedRules'; +import ValidationContext from './ValidationContext'; /** * Implements the "Validation" section of the spec. @@ -85,193 +69,3 @@ function visitUsingRules( visit(documentAST, visitWithTypeInfo(typeInfo, visitInParallel(visitors))); return context.getErrors(); } - -type NodeWithSelectionSet = OperationDefinitionNode | FragmentDefinitionNode; -type VariableUsage = { node: VariableNode, type: ?GraphQLInputType }; - -/** - * An instance of this class is passed as the "this" context to all validators, - * allowing access to commonly useful contextual information from within a - * validation rule. - */ -export class ValidationContext { - _schema: GraphQLSchema; - _ast: DocumentNode; - _typeInfo: TypeInfo; - _errors: Array; - _fragments: ObjMap; - _fragmentSpreads: Map>; - _recursivelyReferencedFragments: Map< - OperationDefinitionNode, - $ReadOnlyArray, - >; - _variableUsages: Map>; - _recursiveVariableUsages: Map< - OperationDefinitionNode, - $ReadOnlyArray, - >; - - constructor( - schema: GraphQLSchema, - ast: DocumentNode, - typeInfo: TypeInfo, - ): void { - this._schema = schema; - this._ast = ast; - this._typeInfo = typeInfo; - this._errors = []; - this._fragmentSpreads = new Map(); - this._recursivelyReferencedFragments = new Map(); - this._variableUsages = new Map(); - this._recursiveVariableUsages = new Map(); - } - - reportError(error: GraphQLError): void { - this._errors.push(error); - } - - getErrors(): $ReadOnlyArray { - return this._errors; - } - - getSchema(): GraphQLSchema { - return this._schema; - } - - getDocument(): DocumentNode { - return this._ast; - } - - getFragment(name: string): ?FragmentDefinitionNode { - let fragments = this._fragments; - if (!fragments) { - this._fragments = fragments = this.getDocument().definitions.reduce( - (frags, statement) => { - if (statement.kind === Kind.FRAGMENT_DEFINITION) { - frags[statement.name.value] = statement; - } - return frags; - }, - Object.create(null), - ); - } - return fragments[name]; - } - - getFragmentSpreads( - node: SelectionSetNode, - ): $ReadOnlyArray { - let spreads = this._fragmentSpreads.get(node); - if (!spreads) { - spreads = []; - const setsToVisit: Array = [node]; - while (setsToVisit.length !== 0) { - const set = setsToVisit.pop(); - for (let i = 0; i < set.selections.length; i++) { - const selection = set.selections[i]; - if (selection.kind === Kind.FRAGMENT_SPREAD) { - spreads.push(selection); - } else if (selection.selectionSet) { - setsToVisit.push(selection.selectionSet); - } - } - } - this._fragmentSpreads.set(node, spreads); - } - return spreads; - } - - getRecursivelyReferencedFragments( - operation: OperationDefinitionNode, - ): $ReadOnlyArray { - let fragments = this._recursivelyReferencedFragments.get(operation); - if (!fragments) { - fragments = []; - const collectedNames = Object.create(null); - const nodesToVisit: Array = [operation.selectionSet]; - while (nodesToVisit.length !== 0) { - const node = nodesToVisit.pop(); - const spreads = this.getFragmentSpreads(node); - for (let i = 0; i < spreads.length; i++) { - const fragName = spreads[i].name.value; - if (collectedNames[fragName] !== true) { - collectedNames[fragName] = true; - const fragment = this.getFragment(fragName); - if (fragment) { - fragments.push(fragment); - nodesToVisit.push(fragment.selectionSet); - } - } - } - } - this._recursivelyReferencedFragments.set(operation, fragments); - } - return fragments; - } - - getVariableUsages(node: NodeWithSelectionSet): $ReadOnlyArray { - let usages = this._variableUsages.get(node); - if (!usages) { - const newUsages = []; - const typeInfo = new TypeInfo(this._schema); - visit( - node, - visitWithTypeInfo(typeInfo, { - VariableDefinition: () => false, - Variable(variable) { - newUsages.push({ node: variable, type: typeInfo.getInputType() }); - }, - }), - ); - usages = newUsages; - this._variableUsages.set(node, usages); - } - return usages; - } - - getRecursiveVariableUsages( - operation: OperationDefinitionNode, - ): $ReadOnlyArray { - let usages = this._recursiveVariableUsages.get(operation); - if (!usages) { - usages = this.getVariableUsages(operation); - const fragments = this.getRecursivelyReferencedFragments(operation); - for (let i = 0; i < fragments.length; i++) { - Array.prototype.push.apply( - usages, - this.getVariableUsages(fragments[i]), - ); - } - this._recursiveVariableUsages.set(operation, usages); - } - return usages; - } - - getType(): ?GraphQLOutputType { - return this._typeInfo.getType(); - } - - getParentType(): ?GraphQLCompositeType { - return this._typeInfo.getParentType(); - } - - getInputType(): ?GraphQLInputType { - return this._typeInfo.getInputType(); - } - - getParentInputType(): ?GraphQLInputType { - return this._typeInfo.getParentInputType(); - } - - getFieldDef(): ?GraphQLField<*, *> { - return this._typeInfo.getFieldDef(); - } - - getDirective(): ?GraphQLDirective { - return this._typeInfo.getDirective(); - } - - getArgument(): ?GraphQLArgument { - return this._typeInfo.getArgument(); - } -}