apollo-server/src/core/runQuery.ts

140 lines
4.4 KiB
TypeScript
Raw Normal View History

import {
GraphQLSchema,
GraphQLResult,
Document,
parse,
print,
validate,
execute,
formatError,
2016-07-02 22:51:17 -07:00
specifiedRules,
ValidationRule,
} from 'graphql';
export interface GqlResponse {
data?: Object;
errors?: Array<string>;
}
export enum LogAction {
request, parse, validation, execute
}
export enum LogStep {
start, end, status
}
export interface LogMessage {
action: LogAction;
step: LogStep;
key?: string;
data?: Object;
}
export interface LogFunction {
(message: LogMessage);
}
export interface QueryOptions {
schema: GraphQLSchema;
query: string | Document;
rootValue?: any;
context?: any;
variables?: { [key: string]: any };
operationName?: string;
logFunction?: LogFunction;
2016-07-02 22:51:17 -07:00
validationRules?: Array<ValidationRule>;
// WARNING: these extra validation rules are only applied to queries
// submitted as string, not those submitted as Document!
formatError?: Function;
formatResponse?: Function;
}
const resolvedPromise = Promise.resolve();
function runQuery(options: QueryOptions): Promise<GraphQLResult> {
// Fiber-aware Promises run their .then callbacks in Fibers.
return resolvedPromise.then(() => doRunQuery(options));
}
function doRunQuery(options: QueryOptions): Promise<GraphQLResult> {
let documentAST: Document;
const logFunction = options.logFunction || function(){ return null; };
logFunction({action: LogAction.request, step: LogStep.start});
2016-07-02 22:51:17 -07:00
function format(errors: Array<Error>): Array<Error> {
// TODO: fix types! shouldn't have to cast.
// the blocker is that the typings aren't right atm:
// GraphQLResult returns Array<GraphQLError>, but the formatError function
// returns Array<GraphQLFormattedError>
return errors.map(options.formatError || formatError as any) as Array<Error>;
}
2016-06-24 17:16:33 -04:00
const qry = typeof options.query === 'string' ? options.query : print(options.query);
logFunction({action: LogAction.request, step: LogStep.status, key: 'query', data: qry});
logFunction({action: LogAction.request, step: LogStep.status, key: 'variables', data: options.variables});
logFunction({action: LogAction.request, step: LogStep.status, key: 'operationName', data: options.operationName});
// if query is already an AST, don't parse or validate
if (typeof options.query === 'string') {
try {
// TODO: time this with log function
logFunction({action: LogAction.parse, step: LogStep.start});
documentAST = parse(options.query as string);
logFunction({action: LogAction.parse, step: LogStep.end});
} catch (syntaxError) {
logFunction({action: LogAction.parse, step: LogStep.end});
2016-07-02 22:51:17 -07:00
return Promise.resolve({ errors: format([syntaxError]) });
}
// TODO: time this with log function
2016-07-02 22:51:17 -07:00
let rules = specifiedRules;
if (options.validationRules) {
rules = rules.concat(options.validationRules);
}
logFunction({action: LogAction.validation, step: LogStep.start});
2016-07-02 22:51:17 -07:00
const validationErrors = validate(options.schema, documentAST, rules);
logFunction({action: LogAction.validation, step: LogStep.end});
if (validationErrors.length) {
2016-07-02 22:51:17 -07:00
return Promise.resolve({ errors: format(validationErrors) });
}
} else {
documentAST = options.query as Document;
}
2016-06-24 16:57:52 -04:00
try {
logFunction({action: LogAction.execute, step: LogStep.start});
2016-06-24 16:57:52 -04:00
return execute(
2016-06-24 17:16:33 -04:00
options.schema,
2016-06-24 16:57:52 -04:00
documentAST,
2016-06-24 17:16:33 -04:00
options.rootValue,
options.context,
options.variables,
options.operationName
).then(gqlResponse => {
logFunction({action: LogAction.execute, step: LogStep.end});
logFunction({action: LogAction.request, step: LogStep.end});
let response = {
data: gqlResponse.data,
};
if (gqlResponse.errors) {
2016-07-02 22:51:17 -07:00
response['errors'] = format(gqlResponse.errors);
}
if (options.formatResponse) {
2016-08-05 11:42:46 -07:00
response = options.formatResponse(response, options);
}
return response;
});
2016-06-24 16:57:52 -04:00
} catch (executionError) {
logFunction({action: LogAction.execute, step: LogStep.end});
logFunction({action: LogAction.request, step: LogStep.end});
2016-07-02 22:51:17 -07:00
return Promise.resolve({ errors: format([executionError]) });
2016-06-24 16:57:52 -04:00
}
}
export { runQuery };