validate.js.flow 3.82 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
// @flow strict

import devAssert from '../jsutils/devAssert';

import { GraphQLError } from '../error/GraphQLError';

import { type DocumentNode } from '../language/ast';
import { visit, visitInParallel, visitWithTypeInfo } from '../language/visitor';

import { type GraphQLSchema } from '../type/schema';
import { assertValidSchema } from '../type/validate';

import { TypeInfo } from '../utilities/TypeInfo';

import { specifiedRules, specifiedSDLRules } from './specifiedRules';
import {
  type SDLValidationRule,
  type ValidationRule,
  SDLValidationContext,
  ValidationContext,
} from './ValidationContext';

export const ABORT_VALIDATION = Object.freeze({});

/**
 * Implements the "Validation" section of the spec.
 *
 * Validation runs synchronously, returning an array of encountered errors, or
 * an empty array if no errors were encountered and the document is valid.
 *
 * A list of specific validation rules may be provided. If not provided, the
 * default list of rules defined by the GraphQL specification will be used.
 *
 * Each validation rules is a function which returns a visitor
 * (see the language/visitor API). Visitor methods are expected to return
 * GraphQLErrors, or Arrays of GraphQLErrors when invalid.
 *
 * Optionally a custom TypeInfo instance may be provided. If not provided, one
 * will be created from the provided schema.
 */
export function validate(
  schema: GraphQLSchema,
  documentAST: DocumentNode,
  rules?: $ReadOnlyArray<ValidationRule> = specifiedRules,
  typeInfo?: TypeInfo = new TypeInfo(schema),
  options?: {| maxErrors?: number |},
): $ReadOnlyArray<GraphQLError> {
  devAssert(documentAST, 'Must provide document');
  // If the schema used for validation is invalid, throw an error.
  assertValidSchema(schema);

  const abortObj = Object.freeze({});
  const errors = [];
  const maxErrors = options && options.maxErrors;
  const context = new ValidationContext(
    schema,
    documentAST,
    typeInfo,
    error => {
      if (maxErrors != null && errors.length >= maxErrors) {
        errors.push(
          new GraphQLError(
            'Too many validation errors, error limit reached. Validation aborted.',
          ),
        );
        throw abortObj;
      }
      errors.push(error);
    },
  );

  // This uses a specialized visitor which runs multiple visitors in parallel,
  // while maintaining the visitor skip and break API.
  const visitor = visitInParallel(rules.map(rule => rule(context)));

  // Visit the whole document with each instance of all provided rules.
  try {
    visit(documentAST, visitWithTypeInfo(typeInfo, visitor));
  } catch (e) {
    if (e !== abortObj) {
      throw e;
    }
  }
  return errors;
}

// @internal
export function validateSDL(
  documentAST: DocumentNode,
  schemaToExtend?: ?GraphQLSchema,
  rules?: $ReadOnlyArray<SDLValidationRule> = specifiedSDLRules,
): $ReadOnlyArray<GraphQLError> {
  const errors = [];
  const context = new SDLValidationContext(
    documentAST,
    schemaToExtend,
    error => {
      errors.push(error);
    },
  );

  const visitors = rules.map(rule => rule(context));
  visit(documentAST, visitInParallel(visitors));
  return errors;
}

/**
 * Utility function which asserts a SDL document is valid by throwing an error
 * if it is invalid.
 *
 * @internal
 */
export function assertValidSDL(documentAST: DocumentNode): void {
  const errors = validateSDL(documentAST);
  if (errors.length !== 0) {
    throw new Error(errors.map(error => error.message).join('\n\n'));
  }
}

/**
 * Utility function which asserts a SDL document is valid by throwing an error
 * if it is invalid.
 *
 * @internal
 */
export function assertValidSDLExtension(
  documentAST: DocumentNode,
  schema: GraphQLSchema,
): void {
  const errors = validateSDL(documentAST, schema);
  if (errors.length !== 0) {
    throw new Error(errors.map(error => error.message).join('\n\n'));
  }
}