-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathaddDirectiveResolveFunctionsToSchema.js
122 lines (105 loc) · 3.76 KB
/
addDirectiveResolveFunctionsToSchema.js
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
import { forEachField } from 'graphql-tools'
import { defaultFieldResolver } from 'graphql'
import * as graphqlLanguage from 'graphql/language'
import * as graphqlType from 'graphql/type'
import { getDirectiveValues } from 'graphql/execution'
const DirectiveLocation =
graphqlLanguage.DirectiveLocation || graphqlType.DirectiveLocation
const BUILT_IN_DIRECTIVES = ['deprecated', 'skip', 'include']
function getFieldResolver(field) {
const resolver = field.resolve || defaultFieldResolver
return resolver.bind(field)
}
function createAsyncResolver(field) {
const originalResolver = getFieldResolver(field)
return async (source, args, context, info) =>
originalResolver(source, args, context, info)
}
function getDirectiveInfo(directive, resolverMap, schema, location, variables) {
const name = directive.name.value
const Directive = schema.getDirective(name)
if (typeof Directive === 'undefined') {
throw new Error(
`Directive @${name} is undefined. ` +
'Please define in schema before using.',
)
}
if (!Directive.locations.includes(location)) {
throw new Error(
`Directive @${name} is not marked to be used on "${location}" location. ` +
`Please add "directive @${name} ON ${location}" in schema.`,
)
}
const resolver = resolverMap[name]
if (!resolver) {
throw new Error(
`Directive @${name} has no resolver.` +
'Please define one using createFieldExecutionResolver().',
)
}
const args = getDirectiveValues(Directive, { directives: [directive] }, variables)
return { args, resolver }
}
function filterCustomDirectives(directives) {
return directives.filter(directive => !BUILT_IN_DIRECTIVES.includes(directive.name.value))
}
function createFieldExecutionResolver(field, resolverMap, schema) {
const directives = filterCustomDirectives(field.astNode.directives)
if (!directives.length) return getFieldResolver(field)
return directives.reduce((recursiveResolver, directive) => {
const directiveInfo = getDirectiveInfo(
directive,
resolverMap,
schema,
DirectiveLocation.FIELD_DEFINITION,
)
return (source, args, context, info) => directiveInfo.resolver(
() => recursiveResolver(source, args, context, info),
source,
directiveInfo.args,
context,
info,
)
}, createAsyncResolver(field))
}
function createFieldResolver(field, resolverMap, schema) {
const originalResolver = getFieldResolver(field)
const asyncResolver = createAsyncResolver(field)
return (source, args, context, info) => {
const directives = filterCustomDirectives(info.fieldNodes[0].directives)
if (!directives.length) return originalResolver(source, args, context, info)
const fieldResolver = directives.reduce((recursiveResolver, directive) => {
const directiveInfo = getDirectiveInfo(
directive,
resolverMap,
schema,
DirectiveLocation.FIELD,
info.variableValues,
)
return () =>
directiveInfo.resolver(
() => recursiveResolver(source, args, context, info),
source,
directiveInfo.args,
context,
info,
)
}, asyncResolver)
return fieldResolver(source, args, context, info)
}
}
function addDirectiveResolveFunctionsToSchema(schema, resolverMap) {
if (typeof resolverMap !== 'object') {
throw new Error(
`Expected resolverMap to be of type object, got ${typeof resolverMap}`,
)
}
if (Array.isArray(resolverMap)) {
throw new Error('Expected resolverMap to be of type object, got Array')
}
forEachField(schema, field => {
field.resolve = createFieldExecutionResolver(field, resolverMap, schema)
field.resolve = createFieldResolver(field, resolverMap, schema)
})
}
export default addDirectiveResolveFunctionsToSchema