Initial Save
This commit is contained in:
10
node_modules/graphql-extensions/CHANGELOG.md
generated
vendored
Normal file
10
node_modules/graphql-extensions/CHANGELOG.md
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# Changelog
|
||||
|
||||
> This package is deprecated. Please use the [Apollo Server Plugin API](https://www.apollographql.com/docs/apollo-server/integrations/plugins/) (specified on the `plugins` property, rather than `extensions`), which provides the same functionality (and more).
|
||||
|
||||
### 0.1.0-beta
|
||||
- *Backwards-incompatible change*: `fooDidStart` handlers (where foo is `request`, `parsing`, `validation`, and `execution`) now return their end handler; the `fooDidEnd` handlers no longer exist. The end handlers now take errors. There is a new `willSendResponse` handler. The `fooDidStart` handlers take extra options (eg, the `ExecutionArgs` for `executionDidStart`).
|
||||
- *Backwards-incompatible change*: Previously, the `GraphQLExtensionStack` constructor took either `GraphQLExtension` objects or their constructors. Now you may only pass in `GraphQLExtension` objects.
|
||||
|
||||
### 0.0.10
|
||||
- Fix lifecycle method invocations on extensions
|
||||
21
node_modules/graphql-extensions/LICENSE
generated
vendored
Normal file
21
node_modules/graphql-extensions/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Apollo Graph, Inc. (Formerly Meteor Development Group, Inc.)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
4
node_modules/graphql-extensions/README.md
generated
vendored
Normal file
4
node_modules/graphql-extensions/README.md
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
# graphql-extensions
|
||||
|
||||
[](https://badge.fury.io/js/graphql-extensions)
|
||||
[](https://circleci.com/gh/apollographql/apollo-server)
|
||||
85
node_modules/graphql-extensions/dist/index.d.ts
generated
vendored
Normal file
85
node_modules/graphql-extensions/dist/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
import { GraphQLSchema, GraphQLField, GraphQLFieldResolver, GraphQLResolveInfo, ExecutionArgs, DocumentNode, GraphQLError } from 'graphql';
|
||||
import { Request } from 'apollo-server-env';
|
||||
import { GraphQLResponse, GraphQLRequestContext } from 'apollo-server-types';
|
||||
export { GraphQLResponse };
|
||||
export declare type EndHandler = (...errors: Array<Error>) => void;
|
||||
export declare class GraphQLExtension<TContext = any> {
|
||||
requestDidStart?(o: {
|
||||
request: Pick<Request, 'url' | 'method' | 'headers'>;
|
||||
queryString?: string;
|
||||
parsedQuery?: DocumentNode;
|
||||
operationName?: string;
|
||||
variables?: {
|
||||
[key: string]: any;
|
||||
};
|
||||
persistedQueryHit?: boolean;
|
||||
persistedQueryRegister?: boolean;
|
||||
context: TContext;
|
||||
requestContext: GraphQLRequestContext<TContext>;
|
||||
}): EndHandler | void;
|
||||
parsingDidStart?(o: {
|
||||
queryString: string;
|
||||
}): EndHandler | void;
|
||||
validationDidStart?(): EndHandler | void;
|
||||
executionDidStart?(o: {
|
||||
executionArgs: ExecutionArgs;
|
||||
}): EndHandler | void;
|
||||
didEncounterErrors?(errors: ReadonlyArray<GraphQLError>): void;
|
||||
willSendResponse?(o: {
|
||||
graphqlResponse: GraphQLResponse;
|
||||
context: TContext;
|
||||
}): void | {
|
||||
graphqlResponse: GraphQLResponse;
|
||||
context: TContext;
|
||||
};
|
||||
willResolveField?(source: any, args: {
|
||||
[argName: string]: any;
|
||||
}, context: TContext, info: GraphQLResolveInfo): ((error: Error | null, result?: any) => void) | void;
|
||||
format?(): [string, any] | undefined;
|
||||
}
|
||||
export declare class GraphQLExtensionStack<TContext = any> {
|
||||
fieldResolver?: GraphQLFieldResolver<any, any>;
|
||||
private extensions;
|
||||
constructor(extensions: GraphQLExtension<TContext>[]);
|
||||
requestDidStart(o: {
|
||||
request: Pick<Request, 'url' | 'method' | 'headers'>;
|
||||
queryString?: string;
|
||||
parsedQuery?: DocumentNode;
|
||||
operationName?: string;
|
||||
variables?: {
|
||||
[key: string]: any;
|
||||
};
|
||||
persistedQueryHit?: boolean;
|
||||
persistedQueryRegister?: boolean;
|
||||
context: TContext;
|
||||
extensions?: Record<string, any>;
|
||||
requestContext: GraphQLRequestContext<TContext>;
|
||||
}): EndHandler;
|
||||
parsingDidStart(o: {
|
||||
queryString: string;
|
||||
}): EndHandler;
|
||||
validationDidStart(): EndHandler;
|
||||
executionDidStart(o: {
|
||||
executionArgs: ExecutionArgs;
|
||||
}): EndHandler;
|
||||
didEncounterErrors(errors: ReadonlyArray<GraphQLError>): void;
|
||||
willSendResponse(o: {
|
||||
graphqlResponse: GraphQLResponse;
|
||||
context: TContext;
|
||||
}): {
|
||||
graphqlResponse: GraphQLResponse;
|
||||
context: TContext;
|
||||
};
|
||||
willResolveField(source: any, args: {
|
||||
[argName: string]: any;
|
||||
}, context: TContext, info: GraphQLResolveInfo): (error: Error | null, result?: any) => void;
|
||||
format(): {};
|
||||
private handleDidStart;
|
||||
}
|
||||
export declare function enableGraphQLExtensions(schema: GraphQLSchema & {
|
||||
_extensionsEnabled?: boolean;
|
||||
}): GraphQLSchema & {
|
||||
_extensionsEnabled?: boolean | undefined;
|
||||
};
|
||||
export declare type FieldIteratorFn = (fieldDef: GraphQLField<any, any>, typeName: string, fieldName: string) => void;
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
1
node_modules/graphql-extensions/dist/index.d.ts.map
generated
vendored
Normal file
1
node_modules/graphql-extensions/dist/index.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
||||
178
node_modules/graphql-extensions/dist/index.js
generated
vendored
Normal file
178
node_modules/graphql-extensions/dist/index.js
generated
vendored
Normal file
@@ -0,0 +1,178 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.enableGraphQLExtensions = exports.GraphQLExtensionStack = exports.GraphQLExtension = void 0;
|
||||
const graphql_1 = require("graphql");
|
||||
class GraphQLExtension {
|
||||
}
|
||||
exports.GraphQLExtension = GraphQLExtension;
|
||||
class GraphQLExtensionStack {
|
||||
constructor(extensions) {
|
||||
this.extensions = extensions;
|
||||
}
|
||||
requestDidStart(o) {
|
||||
return this.handleDidStart(ext => ext.requestDidStart && ext.requestDidStart(o));
|
||||
}
|
||||
parsingDidStart(o) {
|
||||
return this.handleDidStart(ext => ext.parsingDidStart && ext.parsingDidStart(o));
|
||||
}
|
||||
validationDidStart() {
|
||||
return this.handleDidStart(ext => ext.validationDidStart && ext.validationDidStart());
|
||||
}
|
||||
executionDidStart(o) {
|
||||
if (o.executionArgs.fieldResolver) {
|
||||
this.fieldResolver = o.executionArgs.fieldResolver;
|
||||
}
|
||||
return this.handleDidStart(ext => ext.executionDidStart && ext.executionDidStart(o));
|
||||
}
|
||||
didEncounterErrors(errors) {
|
||||
this.extensions.forEach(extension => {
|
||||
if (extension.didEncounterErrors) {
|
||||
extension.didEncounterErrors(errors);
|
||||
}
|
||||
});
|
||||
}
|
||||
willSendResponse(o) {
|
||||
let reference = o;
|
||||
[...this.extensions].reverse().forEach(extension => {
|
||||
if (extension.willSendResponse) {
|
||||
const result = extension.willSendResponse(reference);
|
||||
if (result) {
|
||||
reference = result;
|
||||
}
|
||||
}
|
||||
});
|
||||
return reference;
|
||||
}
|
||||
willResolveField(source, args, context, info) {
|
||||
const handlers = this.extensions
|
||||
.map(extension => extension.willResolveField &&
|
||||
extension.willResolveField(source, args, context, info))
|
||||
.filter(x => x)
|
||||
.reverse();
|
||||
return (error, result) => {
|
||||
for (const handler of handlers) {
|
||||
handler(error, result);
|
||||
}
|
||||
};
|
||||
}
|
||||
format() {
|
||||
return this.extensions
|
||||
.map(extension => extension.format && extension.format())
|
||||
.filter(x => x).reduce((extensions, [key, value]) => Object.assign(extensions, { [key]: value }), {});
|
||||
}
|
||||
handleDidStart(startInvoker) {
|
||||
const endHandlers = [];
|
||||
this.extensions.forEach(extension => {
|
||||
try {
|
||||
const endHandler = startInvoker(extension);
|
||||
if (endHandler) {
|
||||
endHandlers.push(endHandler);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
});
|
||||
return (...errors) => {
|
||||
endHandlers.reverse();
|
||||
for (const endHandler of endHandlers) {
|
||||
try {
|
||||
endHandler(...errors);
|
||||
}
|
||||
catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
exports.GraphQLExtensionStack = GraphQLExtensionStack;
|
||||
function enableGraphQLExtensions(schema) {
|
||||
if (schema._extensionsEnabled) {
|
||||
return schema;
|
||||
}
|
||||
schema._extensionsEnabled = true;
|
||||
forEachField(schema, wrapField);
|
||||
return schema;
|
||||
}
|
||||
exports.enableGraphQLExtensions = enableGraphQLExtensions;
|
||||
function wrapField(field) {
|
||||
const fieldResolver = field.resolve;
|
||||
field.resolve = (source, args, context, info) => {
|
||||
const parentPath = info.path.prev;
|
||||
const extensionStack = context && context._extensionStack;
|
||||
const handler = (extensionStack &&
|
||||
extensionStack.willResolveField(source, args, context, info)) ||
|
||||
((_err, _result) => {
|
||||
});
|
||||
const resolveObject = info.parentType.resolveObject;
|
||||
let whenObjectResolved;
|
||||
if (parentPath && resolveObject) {
|
||||
if (!parentPath.__fields) {
|
||||
parentPath.__fields = {};
|
||||
}
|
||||
parentPath.__fields[info.fieldName] = info.fieldNodes;
|
||||
whenObjectResolved = parentPath.__whenObjectResolved;
|
||||
if (!whenObjectResolved) {
|
||||
whenObjectResolved = Promise.resolve().then(() => {
|
||||
return resolveObject(source, parentPath.__fields, context, info);
|
||||
});
|
||||
parentPath.__whenObjectResolved = whenObjectResolved;
|
||||
}
|
||||
}
|
||||
try {
|
||||
const actualFieldResolver = fieldResolver ||
|
||||
(extensionStack && extensionStack.fieldResolver) ||
|
||||
graphql_1.defaultFieldResolver;
|
||||
let result;
|
||||
if (whenObjectResolved) {
|
||||
result = whenObjectResolved.then((resolvedObject) => {
|
||||
return actualFieldResolver(resolvedObject, args, context, info);
|
||||
});
|
||||
}
|
||||
else {
|
||||
result = actualFieldResolver(source, args, context, info);
|
||||
}
|
||||
whenResultIsFinished(result, handler);
|
||||
return result;
|
||||
}
|
||||
catch (error) {
|
||||
handler(error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
function isPromise(x) {
|
||||
return x && typeof x.then === 'function';
|
||||
}
|
||||
function whenResultIsFinished(result, callback) {
|
||||
if (isPromise(result)) {
|
||||
result.then((r) => callback(null, r), (err) => callback(err));
|
||||
}
|
||||
else if (Array.isArray(result)) {
|
||||
if (result.some(isPromise)) {
|
||||
Promise.all(result).then((r) => callback(null, r), (err) => callback(err));
|
||||
}
|
||||
else {
|
||||
callback(null, result);
|
||||
}
|
||||
}
|
||||
else {
|
||||
callback(null, result);
|
||||
}
|
||||
}
|
||||
function forEachField(schema, fn) {
|
||||
const typeMap = schema.getTypeMap();
|
||||
Object.keys(typeMap).forEach(typeName => {
|
||||
const type = typeMap[typeName];
|
||||
if (!graphql_1.getNamedType(type).name.startsWith('__') &&
|
||||
type instanceof graphql_1.GraphQLObjectType) {
|
||||
const fields = type.getFields();
|
||||
Object.keys(fields).forEach(fieldName => {
|
||||
const field = fields[fieldName];
|
||||
fn(field, typeName, fieldName);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
//# sourceMappingURL=index.js.map
|
||||
1
node_modules/graphql-extensions/dist/index.js.map
generated
vendored
Normal file
1
node_modules/graphql-extensions/dist/index.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
26
node_modules/graphql-extensions/package.json
generated
vendored
Normal file
26
node_modules/graphql-extensions/package.json
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "graphql-extensions",
|
||||
"version": "0.16.0",
|
||||
"description": "Add extensions to GraphQL servers",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "apollographql/apollo-server",
|
||||
"directory": "packages/graphql-extensions"
|
||||
},
|
||||
"author": "Apollo <opensource@apollographql.com>",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@apollographql/apollo-tools": "^0.5.0",
|
||||
"apollo-server-env": "^3.2.0",
|
||||
"apollo-server-types": "^0.10.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"graphql": "^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0"
|
||||
},
|
||||
"gitHead": "91de501bb389c07ccfc5e684811153267b91e9ac"
|
||||
}
|
||||
341
node_modules/graphql-extensions/src/index.ts
generated
vendored
Normal file
341
node_modules/graphql-extensions/src/index.ts
generated
vendored
Normal file
@@ -0,0 +1,341 @@
|
||||
import {
|
||||
GraphQLSchema,
|
||||
GraphQLObjectType,
|
||||
getNamedType,
|
||||
GraphQLField,
|
||||
defaultFieldResolver,
|
||||
GraphQLFieldResolver,
|
||||
GraphQLResolveInfo,
|
||||
ExecutionArgs,
|
||||
DocumentNode,
|
||||
ResponsePath,
|
||||
FieldNode,
|
||||
GraphQLError,
|
||||
} from 'graphql';
|
||||
|
||||
import { Request } from 'apollo-server-env';
|
||||
|
||||
import { GraphQLResponse, GraphQLRequestContext } from 'apollo-server-types';
|
||||
export { GraphQLResponse };
|
||||
|
||||
import { GraphQLObjectResolver } from '@apollographql/apollo-tools';
|
||||
|
||||
export type EndHandler = (...errors: Array<Error>) => void;
|
||||
// A StartHandlerInvoker is a function that, given a specific GraphQLExtension,
|
||||
// finds a specific StartHandler on that extension and calls it with appropriate
|
||||
// arguments.
|
||||
type StartHandlerInvoker<TContext = any> = (
|
||||
ext: GraphQLExtension<TContext>,
|
||||
) => EndHandler | void;
|
||||
|
||||
export class GraphQLExtension<TContext = any> {
|
||||
public requestDidStart?(o: {
|
||||
request: Pick<Request, 'url' | 'method' | 'headers'>;
|
||||
queryString?: string;
|
||||
parsedQuery?: DocumentNode;
|
||||
operationName?: string;
|
||||
variables?: { [key: string]: any };
|
||||
persistedQueryHit?: boolean;
|
||||
persistedQueryRegister?: boolean;
|
||||
context: TContext;
|
||||
requestContext: GraphQLRequestContext<TContext>;
|
||||
}): EndHandler | void;
|
||||
public parsingDidStart?(o: { queryString: string }): EndHandler | void;
|
||||
public validationDidStart?(): EndHandler | void;
|
||||
public executionDidStart?(o: {
|
||||
executionArgs: ExecutionArgs;
|
||||
}): EndHandler | void;
|
||||
|
||||
public didEncounterErrors?(errors: ReadonlyArray<GraphQLError>): void;
|
||||
|
||||
public willSendResponse?(o: {
|
||||
graphqlResponse: GraphQLResponse;
|
||||
context: TContext;
|
||||
}): void | { graphqlResponse: GraphQLResponse; context: TContext };
|
||||
|
||||
public willResolveField?(
|
||||
source: any,
|
||||
args: { [argName: string]: any },
|
||||
context: TContext,
|
||||
info: GraphQLResolveInfo,
|
||||
): ((error: Error | null, result?: any) => void) | void;
|
||||
|
||||
public format?(): [string, any] | undefined;
|
||||
}
|
||||
|
||||
export class GraphQLExtensionStack<TContext = any> {
|
||||
public fieldResolver?: GraphQLFieldResolver<any, any>;
|
||||
|
||||
private extensions: GraphQLExtension<TContext>[];
|
||||
|
||||
constructor(extensions: GraphQLExtension<TContext>[]) {
|
||||
this.extensions = extensions;
|
||||
}
|
||||
|
||||
public requestDidStart(o: {
|
||||
request: Pick<Request, 'url' | 'method' | 'headers'>;
|
||||
queryString?: string;
|
||||
parsedQuery?: DocumentNode;
|
||||
operationName?: string;
|
||||
variables?: { [key: string]: any };
|
||||
persistedQueryHit?: boolean;
|
||||
persistedQueryRegister?: boolean;
|
||||
context: TContext;
|
||||
extensions?: Record<string, any>;
|
||||
requestContext: GraphQLRequestContext<TContext>;
|
||||
}): EndHandler {
|
||||
return this.handleDidStart(
|
||||
ext => ext.requestDidStart && ext.requestDidStart(o),
|
||||
);
|
||||
}
|
||||
public parsingDidStart(o: { queryString: string }): EndHandler {
|
||||
return this.handleDidStart(
|
||||
ext => ext.parsingDidStart && ext.parsingDidStart(o),
|
||||
);
|
||||
}
|
||||
public validationDidStart(): EndHandler {
|
||||
return this.handleDidStart(
|
||||
ext => ext.validationDidStart && ext.validationDidStart(),
|
||||
);
|
||||
}
|
||||
public executionDidStart(o: { executionArgs: ExecutionArgs }): EndHandler {
|
||||
if (o.executionArgs.fieldResolver) {
|
||||
this.fieldResolver = o.executionArgs.fieldResolver;
|
||||
}
|
||||
return this.handleDidStart(
|
||||
ext => ext.executionDidStart && ext.executionDidStart(o),
|
||||
);
|
||||
}
|
||||
|
||||
public didEncounterErrors(errors: ReadonlyArray<GraphQLError>) {
|
||||
this.extensions.forEach(extension => {
|
||||
if (extension.didEncounterErrors) {
|
||||
extension.didEncounterErrors(errors);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public willSendResponse(o: {
|
||||
graphqlResponse: GraphQLResponse;
|
||||
context: TContext;
|
||||
}): { graphqlResponse: GraphQLResponse; context: TContext } {
|
||||
let reference = o;
|
||||
// Reverse the array, since this is functions as an end handler
|
||||
[...this.extensions].reverse().forEach(extension => {
|
||||
if (extension.willSendResponse) {
|
||||
const result = extension.willSendResponse(reference);
|
||||
if (result) {
|
||||
reference = result;
|
||||
}
|
||||
}
|
||||
});
|
||||
return reference;
|
||||
}
|
||||
|
||||
public willResolveField(
|
||||
source: any,
|
||||
args: { [argName: string]: any },
|
||||
context: TContext,
|
||||
info: GraphQLResolveInfo,
|
||||
) {
|
||||
const handlers = this.extensions
|
||||
.map(
|
||||
extension =>
|
||||
extension.willResolveField &&
|
||||
extension.willResolveField(source, args, context, info),
|
||||
)
|
||||
.filter(x => x)
|
||||
// Reverse list so that handlers "nest", like in handleDidStart.
|
||||
.reverse() as ((error: Error | null, result?: any) => void)[];
|
||||
|
||||
return (error: Error | null, result?: any) => {
|
||||
for (const handler of handlers) {
|
||||
handler(error, result);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public format() {
|
||||
return (this.extensions
|
||||
.map(extension => extension.format && extension.format())
|
||||
.filter(x => x) as [string, any][]).reduce(
|
||||
(extensions, [key, value]) => Object.assign(extensions, { [key]: value }),
|
||||
{},
|
||||
);
|
||||
}
|
||||
|
||||
private handleDidStart(startInvoker: StartHandlerInvoker): EndHandler {
|
||||
const endHandlers: EndHandler[] = [];
|
||||
this.extensions.forEach(extension => {
|
||||
// Invoke the start handler, which may return an end handler.
|
||||
try {
|
||||
const endHandler = startInvoker(extension);
|
||||
if (endHandler) {
|
||||
endHandlers.push(endHandler);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
});
|
||||
return (...errors: Array<Error>) => {
|
||||
// We run end handlers in reverse order of start handlers. That way, the
|
||||
// first handler in the stack "surrounds" the entire event's process
|
||||
// (helpful for tracing/reporting!)
|
||||
endHandlers.reverse();
|
||||
for (const endHandler of endHandlers) {
|
||||
try {
|
||||
endHandler(...errors);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function enableGraphQLExtensions(
|
||||
schema: GraphQLSchema & { _extensionsEnabled?: boolean },
|
||||
) {
|
||||
if (schema._extensionsEnabled) {
|
||||
return schema;
|
||||
}
|
||||
schema._extensionsEnabled = true;
|
||||
|
||||
forEachField(schema, wrapField);
|
||||
|
||||
return schema;
|
||||
}
|
||||
|
||||
function wrapField(field: GraphQLField<any, any>): void {
|
||||
const fieldResolver = field.resolve;
|
||||
|
||||
field.resolve = (source, args, context, info) => {
|
||||
// This is a bit of a hack, but since `ResponsePath` is a linked list,
|
||||
// a new object gets created every time a path segment is added.
|
||||
// So we can use that to share our `whenObjectResolved` promise across
|
||||
// all field resolvers for the same object.
|
||||
const parentPath = info.path.prev as ResponsePath & {
|
||||
__fields?: Record<string, ReadonlyArray<FieldNode>>;
|
||||
__whenObjectResolved?: Promise<any>;
|
||||
};
|
||||
|
||||
const extensionStack = context && context._extensionStack;
|
||||
const handler =
|
||||
(extensionStack &&
|
||||
extensionStack.willResolveField(source, args, context, info)) ||
|
||||
((_err: Error | null, _result?: any) => {
|
||||
/* do nothing */
|
||||
});
|
||||
|
||||
const resolveObject: GraphQLObjectResolver<
|
||||
any,
|
||||
any
|
||||
> = (info.parentType as any).resolveObject;
|
||||
|
||||
let whenObjectResolved: Promise<any> | undefined;
|
||||
|
||||
if (parentPath && resolveObject) {
|
||||
if (!parentPath.__fields) {
|
||||
parentPath.__fields = {};
|
||||
}
|
||||
|
||||
parentPath.__fields[info.fieldName] = info.fieldNodes;
|
||||
|
||||
whenObjectResolved = parentPath.__whenObjectResolved;
|
||||
if (!whenObjectResolved) {
|
||||
// Use `Promise.resolve().then()` to delay executing
|
||||
// `resolveObject()` so we can collect all the fields first.
|
||||
whenObjectResolved = Promise.resolve().then(() => {
|
||||
return resolveObject(source, parentPath.__fields!, context, info);
|
||||
});
|
||||
parentPath.__whenObjectResolved = whenObjectResolved;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// If no resolver has been defined for a field, use either the configured
|
||||
// field resolver or the default field resolver
|
||||
// (which matches the behavior of graphql-js when there is no explicit
|
||||
// resolve function defined).
|
||||
// XXX: Can't this be pulled up to the top of `wrapField` and only
|
||||
// assigned once? It seems `extensionStack.fieldResolver` isn't set
|
||||
// anywhere?
|
||||
const actualFieldResolver =
|
||||
fieldResolver ||
|
||||
(extensionStack && extensionStack.fieldResolver) ||
|
||||
defaultFieldResolver;
|
||||
|
||||
let result: any;
|
||||
if (whenObjectResolved) {
|
||||
result = whenObjectResolved.then((resolvedObject: any) => {
|
||||
return actualFieldResolver(resolvedObject, args, context, info);
|
||||
});
|
||||
} else {
|
||||
result = actualFieldResolver(source, args, context, info);
|
||||
}
|
||||
|
||||
// Call the stack's handlers either immediately (if result is not a
|
||||
// Promise) or once the Promise is done. Then return that same
|
||||
// maybe-Promise value.
|
||||
whenResultIsFinished(result, handler);
|
||||
return result;
|
||||
} catch (error) {
|
||||
// Normally it's a bad sign to see an error both handled and
|
||||
// re-thrown. But it is useful to allow extensions to track errors while
|
||||
// still handling them in the normal GraphQL way.
|
||||
handler(error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function isPromise(x: any): boolean {
|
||||
return x && typeof x.then === 'function';
|
||||
}
|
||||
|
||||
// Given result (which may be a Promise or an array some of whose elements are
|
||||
// promises) Promises, set up 'callback' to be invoked when result is fully
|
||||
// resolved.
|
||||
function whenResultIsFinished(
|
||||
result: any,
|
||||
callback: (err: Error | null, result?: any) => void,
|
||||
) {
|
||||
if (isPromise(result)) {
|
||||
result.then((r: any) => callback(null, r), (err: Error) => callback(err));
|
||||
} else if (Array.isArray(result)) {
|
||||
if (result.some(isPromise)) {
|
||||
Promise.all(result).then(
|
||||
(r: any) => callback(null, r),
|
||||
(err: Error) => callback(err),
|
||||
);
|
||||
} else {
|
||||
callback(null, result);
|
||||
}
|
||||
} else {
|
||||
callback(null, result);
|
||||
}
|
||||
}
|
||||
|
||||
function forEachField(schema: GraphQLSchema, fn: FieldIteratorFn): void {
|
||||
const typeMap = schema.getTypeMap();
|
||||
Object.keys(typeMap).forEach(typeName => {
|
||||
const type = typeMap[typeName];
|
||||
|
||||
if (
|
||||
!getNamedType(type).name.startsWith('__') &&
|
||||
type instanceof GraphQLObjectType
|
||||
) {
|
||||
const fields = type.getFields();
|
||||
Object.keys(fields).forEach(fieldName => {
|
||||
const field = fields[fieldName];
|
||||
fn(field, typeName, fieldName);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export type FieldIteratorFn = (
|
||||
fieldDef: GraphQLField<any, any>,
|
||||
typeName: string,
|
||||
fieldName: string,
|
||||
) => void;
|
||||
Reference in New Issue
Block a user