Skip to content

Commit

Permalink
Allows buildASTSchema to throw errors with source locations.
Browse files Browse the repository at this point in the history
  • Loading branch information
tcr committed Feb 17, 2017
1 parent f7b94d1 commit fa6ee06
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 7 deletions.
21 changes: 21 additions & 0 deletions src/error/syntaxError.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import { getLocation } from '../language/location';
import type { Source } from '../language/source';
import { GraphQLError } from './GraphQLError';
import type { ASTNode } from '../language/ast';

/**
* Produces a GraphQLError representing a syntax error, containing useful
Expand All @@ -32,6 +33,26 @@ export function syntaxError(
return error;
}

/**
* Produces a string for the invarant(...) function that renders the location
* where an error occurred. If no source is passed in, it renders the message
* without context.
*/
export function invariantError(
message: string,
node: ASTNode,
source: ?Source
): string {
const position = node.loc ? node.loc.start : null;
if (position != null && source != null) {
const location = getLocation(source, position);
return `${message} (${location.line}:${location.column}) ` +
'\n\n' + highlightSourceAtLocation(source, location);
} else {
return message;
}
}

/**
* Render a helpful description of the location of the error in the GraphQL
* Source document.
Expand Down
21 changes: 14 additions & 7 deletions src/utilities/buildASTSchema.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ import invariant from '../jsutils/invariant';
import keyValMap from '../jsutils/keyValMap';
import { valueFromAST } from './valueFromAST';
import { TokenKind } from '../language/lexer';
import { getLocation } from '../language/location';
import { parse } from '../language/parser';
import type { Source } from '../language/source';
import { Source } from '../language/source';
import { getArgumentValues } from '../execution/values';
import { invariantError } from '../error/syntaxError';

import {
LIST_TYPE,
Expand Down Expand Up @@ -135,7 +137,7 @@ function getNamedTypeNode(typeNode: TypeNode): NamedTypeNode {
* Given that AST it constructs a GraphQLSchema. The resulting schema
* has no resolve methods, so execution will use default resolvers.
*/
export function buildASTSchema(ast: DocumentNode): GraphQLSchema {
export function buildASTSchema(ast: DocumentNode, source?: Source): GraphQLSchema {
if (!ast || ast.kind !== DOCUMENT) {
throw new Error('Must provide a document ast.');
}
Expand Down Expand Up @@ -300,25 +302,29 @@ export function buildASTSchema(ast: DocumentNode): GraphQLSchema {

function produceInputType(typeNode: TypeNode): GraphQLInputType {
const type = produceType(typeNode);
invariant(isInputType(type), 'Expected Input type.');
invariant(isInputType(type),
invariantError('Expected Input type', typeNode, source));
return (type: any);
}

function produceOutputType(typeNode: TypeNode): GraphQLOutputType {
const type = produceType(typeNode);
invariant(isOutputType(type), 'Expected Output type.');
invariant(isOutputType(type),
invariantError('Expected Output type', typeNode, source));
return (type: any);
}

function produceObjectType(typeNode: TypeNode): GraphQLObjectType {
const type = produceType(typeNode);
invariant(type instanceof GraphQLObjectType, 'Expected Object type.');
invariant(type instanceof GraphQLObjectType,
invariantError('Expected Object type', typeNode, source));
return type;
}

function produceInterfaceType(typeNode: TypeNode): GraphQLInterfaceType {
const type = produceType(typeNode);
invariant(type instanceof GraphQLInterfaceType, 'Expected Interface type.');
invariant(type instanceof GraphQLInterfaceType,
invariantError('Expected Interface type', typeNode, source));
return type;
}

Expand Down Expand Up @@ -518,7 +524,8 @@ export function getDescription(node: { loc?: Location }): ?string {
* document.
*/
export function buildSchema(source: string | Source): GraphQLSchema {
return buildASTSchema(parse(source));
const sourceObj = typeof source === 'string' ? new Source(source) : source;
return buildASTSchema(parse(sourceObj), sourceObj);
}

// Count the number of spaces on the starting side of a string.
Expand Down

0 comments on commit fa6ee06

Please sign in to comment.