-
Notifications
You must be signed in to change notification settings - Fork 130
/
Copy pathindex.ts
106 lines (93 loc) · 3.08 KB
/
index.ts
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
import {
defaultFieldResolver,
GraphQLResolveInfo,
GraphQLSchema,
isIntrospectionType,
isObjectType,
} from 'graphql';
import { Plugin, PromiseOrValue } from '@envelop/core';
export type Resolver<Context = unknown> = (
root: unknown,
args: Record<string, unknown>,
context: Context,
info: GraphQLResolveInfo,
) => PromiseOrValue<unknown>;
export type AfterResolver = (options: {
result: unknown;
setResult: (newResult: unknown) => void;
}) => PromiseOrValue<void>;
export interface OnResolveOptions<PluginContext extends Record<string, any> = {}> {
context: PluginContext;
root: unknown;
args: Record<string, unknown>;
info: GraphQLResolveInfo;
resolver: Resolver<PluginContext>;
replaceResolver: (newResolver: Resolver<PluginContext>) => void;
}
export type OnResolve<PluginContext extends Record<string, any> = {}> = (
options: OnResolveOptions<PluginContext>,
) => PromiseOrValue<AfterResolver | void>;
export type UseOnResolveOptions = {
/**
* Skip executing the `onResolve` hook on introspection queries.
*
* @default true
*/
skipIntrospection?: boolean;
};
/**
* Wraps the provided schema by hooking into the resolvers of every field.
*
* Use the `onResolve` argument to manipulate the resolver and its results/errors.
*/
export function useOnResolve<PluginContext extends Record<string, any> = {}>(
onResolve: OnResolve<PluginContext>,
opts: UseOnResolveOptions = { skipIntrospection: true },
): Plugin<PluginContext> {
const hasWrappedResolveSymbol = Symbol('hasWrappedResolve');
return {
onSchemaChange({ schema: _schema }) {
const schema = _schema as GraphQLSchema;
if (!schema) return; // nothing to do if schema is missing
for (const type of Object.values(schema.getTypeMap())) {
if ((!opts.skipIntrospection || !isIntrospectionType(type)) && isObjectType(type)) {
for (const field of Object.values(type.getFields())) {
if (field[hasWrappedResolveSymbol]) continue;
let resolver = (field.resolve || defaultFieldResolver) as Resolver<PluginContext>;
field.resolve = async (root, args, context, info) => {
const afterResolve = await onResolve({
root,
args,
context,
info,
resolver,
replaceResolver: newResolver => {
resolver = newResolver;
},
});
let result;
try {
result = await resolver(root, args, context, info);
} catch (err) {
result = err as Error;
}
if (typeof afterResolve === 'function') {
await afterResolve({
result,
setResult: newResult => {
result = newResult;
},
});
}
if (result instanceof Error) {
throw result;
}
return result;
};
field[hasWrappedResolveSymbol] = true;
}
}
}
},
};
}