UNPKG

17.4 kBJavaScriptView Raw
1import { GraphQLEnumType, GraphQLScalarType, GraphQLUnionType, GraphQLInterfaceType, GraphQLObjectType, isSpecifiedScalarType, isScalarType, isEnumType, isUnionType, isInterfaceType, isObjectType, } from 'graphql';
2import { mapSchema, MapperKind, forEachDefaultValue, serializeInputValue, healSchema, parseInputValue, forEachField, } from '@graphql-tools/utils';
3import { checkForResolveTypeResolver } from './checkForResolveTypeResolver.js';
4import { extendResolversFromInterfaces } from './extendResolversFromInterfaces.js';
5export function addResolversToSchema({ schema, resolvers: inputResolvers, defaultFieldResolver, resolverValidationOptions = {}, inheritResolversFromInterfaces = false, updateResolversInPlace = false, }) {
6 const { requireResolversToMatchSchema = 'error', requireResolversForResolveType } = resolverValidationOptions;
7 const resolvers = inheritResolversFromInterfaces
8 ? extendResolversFromInterfaces(schema, inputResolvers)
9 : inputResolvers;
10 for (const typeName in resolvers) {
11 const resolverValue = resolvers[typeName];
12 const resolverType = typeof resolverValue;
13 if (resolverType !== 'object') {
14 throw new Error(`"${typeName}" defined in resolvers, but has invalid value "${resolverValue}". The resolver's value must be of type object.`);
15 }
16 const type = schema.getType(typeName);
17 if (type == null) {
18 if (requireResolversToMatchSchema === 'ignore') {
19 continue;
20 }
21 throw new Error(`"${typeName}" defined in resolvers, but not in schema`);
22 }
23 else if (isSpecifiedScalarType(type)) {
24 // allow -- without recommending -- overriding of specified scalar types
25 for (const fieldName in resolverValue) {
26 if (fieldName.startsWith('__')) {
27 type[fieldName.substring(2)] = resolverValue[fieldName];
28 }
29 else {
30 type[fieldName] = resolverValue[fieldName];
31 }
32 }
33 }
34 else if (isEnumType(type)) {
35 const values = type.getValues();
36 for (const fieldName in resolverValue) {
37 if (!fieldName.startsWith('__') &&
38 !values.some(value => value.name === fieldName) &&
39 requireResolversToMatchSchema &&
40 requireResolversToMatchSchema !== 'ignore') {
41 throw new Error(`${type.name}.${fieldName} was defined in resolvers, but not present within ${type.name}`);
42 }
43 }
44 }
45 else if (isUnionType(type)) {
46 for (const fieldName in resolverValue) {
47 if (!fieldName.startsWith('__') &&
48 requireResolversToMatchSchema &&
49 requireResolversToMatchSchema !== 'ignore') {
50 throw new Error(`${type.name}.${fieldName} was defined in resolvers, but ${type.name} is not an object or interface type`);
51 }
52 }
53 }
54 else if (isObjectType(type) || isInterfaceType(type)) {
55 for (const fieldName in resolverValue) {
56 if (!fieldName.startsWith('__')) {
57 const fields = type.getFields();
58 const field = fields[fieldName];
59 if (field == null) {
60 // Field present in resolver but not in schema
61 if (requireResolversToMatchSchema && requireResolversToMatchSchema !== 'ignore') {
62 throw new Error(`${typeName}.${fieldName} defined in resolvers, but not in schema`);
63 }
64 }
65 else {
66 // Field present in both the resolver and schema
67 const fieldResolve = resolverValue[fieldName];
68 if (typeof fieldResolve !== 'function' && typeof fieldResolve !== 'object') {
69 throw new Error(`Resolver ${typeName}.${fieldName} must be object or function`);
70 }
71 }
72 }
73 }
74 }
75 }
76 schema = updateResolversInPlace
77 ? addResolversToExistingSchema(schema, resolvers, defaultFieldResolver)
78 : createNewSchemaWithResolvers(schema, resolvers, defaultFieldResolver);
79 if (requireResolversForResolveType && requireResolversForResolveType !== 'ignore') {
80 checkForResolveTypeResolver(schema, requireResolversForResolveType);
81 }
82 return schema;
83}
84function addResolversToExistingSchema(schema, resolvers, defaultFieldResolver) {
85 var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
86 const typeMap = schema.getTypeMap();
87 for (const typeName in resolvers) {
88 const type = schema.getType(typeName);
89 const resolverValue = resolvers[typeName];
90 if (isScalarType(type)) {
91 for (const fieldName in resolverValue) {
92 if (fieldName.startsWith('__')) {
93 type[fieldName.substring(2)] = resolverValue[fieldName];
94 }
95 else if (fieldName === 'astNode' && type.astNode != null) {
96 type.astNode = {
97 ...type.astNode,
98 description: (_b = (_a = resolverValue === null || resolverValue === void 0 ? void 0 : resolverValue.astNode) === null || _a === void 0 ? void 0 : _a.description) !== null && _b !== void 0 ? _b : type.astNode.description,
99 directives: ((_c = type.astNode.directives) !== null && _c !== void 0 ? _c : []).concat((_e = (_d = resolverValue === null || resolverValue === void 0 ? void 0 : resolverValue.astNode) === null || _d === void 0 ? void 0 : _d.directives) !== null && _e !== void 0 ? _e : []),
100 };
101 }
102 else if (fieldName === 'extensionASTNodes' && type.extensionASTNodes != null) {
103 type.extensionASTNodes = type.extensionASTNodes.concat((_f = resolverValue === null || resolverValue === void 0 ? void 0 : resolverValue.extensionASTNodes) !== null && _f !== void 0 ? _f : []);
104 }
105 else if (fieldName === 'extensions' &&
106 type.extensions != null &&
107 resolverValue.extensions != null) {
108 type.extensions = Object.assign(Object.create(null), type.extensions, resolverValue.extensions);
109 }
110 else {
111 type[fieldName] = resolverValue[fieldName];
112 }
113 }
114 }
115 else if (isEnumType(type)) {
116 const config = type.toConfig();
117 const enumValueConfigMap = config.values;
118 for (const fieldName in resolverValue) {
119 if (fieldName.startsWith('__')) {
120 config[fieldName.substring(2)] = resolverValue[fieldName];
121 }
122 else if (fieldName === 'astNode' && config.astNode != null) {
123 config.astNode = {
124 ...config.astNode,
125 description: (_h = (_g = resolverValue === null || resolverValue === void 0 ? void 0 : resolverValue.astNode) === null || _g === void 0 ? void 0 : _g.description) !== null && _h !== void 0 ? _h : config.astNode.description,
126 directives: ((_j = config.astNode.directives) !== null && _j !== void 0 ? _j : []).concat((_l = (_k = resolverValue === null || resolverValue === void 0 ? void 0 : resolverValue.astNode) === null || _k === void 0 ? void 0 : _k.directives) !== null && _l !== void 0 ? _l : []),
127 };
128 }
129 else if (fieldName === 'extensionASTNodes' && config.extensionASTNodes != null) {
130 config.extensionASTNodes = config.extensionASTNodes.concat((_m = resolverValue === null || resolverValue === void 0 ? void 0 : resolverValue.extensionASTNodes) !== null && _m !== void 0 ? _m : []);
131 }
132 else if (fieldName === 'extensions' &&
133 type.extensions != null &&
134 resolverValue.extensions != null) {
135 type.extensions = Object.assign(Object.create(null), type.extensions, resolverValue.extensions);
136 }
137 else if (enumValueConfigMap[fieldName]) {
138 enumValueConfigMap[fieldName].value = resolverValue[fieldName];
139 }
140 }
141 typeMap[typeName] = new GraphQLEnumType(config);
142 }
143 else if (isUnionType(type)) {
144 for (const fieldName in resolverValue) {
145 if (fieldName.startsWith('__')) {
146 type[fieldName.substring(2)] = resolverValue[fieldName];
147 }
148 }
149 }
150 else if (isObjectType(type) || isInterfaceType(type)) {
151 for (const fieldName in resolverValue) {
152 if (fieldName.startsWith('__')) {
153 // this is for isTypeOf and resolveType and all the other stuff.
154 type[fieldName.substring(2)] = resolverValue[fieldName];
155 continue;
156 }
157 const fields = type.getFields();
158 const field = fields[fieldName];
159 if (field != null) {
160 const fieldResolve = resolverValue[fieldName];
161 if (typeof fieldResolve === 'function') {
162 // for convenience. Allows shorter syntax in resolver definition file
163 field.resolve = fieldResolve.bind(resolverValue);
164 }
165 else {
166 setFieldProperties(field, fieldResolve);
167 }
168 }
169 }
170 }
171 }
172 // serialize all default values prior to healing fields with new scalar/enum types.
173 forEachDefaultValue(schema, serializeInputValue);
174 // schema may have new scalar/enum types that require healing
175 healSchema(schema);
176 // reparse all default values with new parsing functions.
177 forEachDefaultValue(schema, parseInputValue);
178 if (defaultFieldResolver != null) {
179 forEachField(schema, field => {
180 if (!field.resolve) {
181 field.resolve = defaultFieldResolver;
182 }
183 });
184 }
185 return schema;
186}
187function createNewSchemaWithResolvers(schema, resolvers, defaultFieldResolver) {
188 schema = mapSchema(schema, {
189 [MapperKind.SCALAR_TYPE]: type => {
190 var _a, _b, _c, _d, _e, _f;
191 const config = type.toConfig();
192 const resolverValue = resolvers[type.name];
193 if (!isSpecifiedScalarType(type) && resolverValue != null) {
194 for (const fieldName in resolverValue) {
195 if (fieldName.startsWith('__')) {
196 config[fieldName.substring(2)] = resolverValue[fieldName];
197 }
198 else if (fieldName === 'astNode' && config.astNode != null) {
199 config.astNode = {
200 ...config.astNode,
201 description: (_b = (_a = resolverValue === null || resolverValue === void 0 ? void 0 : resolverValue.astNode) === null || _a === void 0 ? void 0 : _a.description) !== null && _b !== void 0 ? _b : config.astNode.description,
202 directives: ((_c = config.astNode.directives) !== null && _c !== void 0 ? _c : []).concat((_e = (_d = resolverValue === null || resolverValue === void 0 ? void 0 : resolverValue.astNode) === null || _d === void 0 ? void 0 : _d.directives) !== null && _e !== void 0 ? _e : []),
203 };
204 }
205 else if (fieldName === 'extensionASTNodes' && config.extensionASTNodes != null) {
206 config.extensionASTNodes = config.extensionASTNodes.concat((_f = resolverValue === null || resolverValue === void 0 ? void 0 : resolverValue.extensionASTNodes) !== null && _f !== void 0 ? _f : []);
207 }
208 else if (fieldName === 'extensions' &&
209 config.extensions != null &&
210 resolverValue.extensions != null) {
211 config.extensions = Object.assign(Object.create(null), type.extensions, resolverValue.extensions);
212 }
213 else {
214 config[fieldName] = resolverValue[fieldName];
215 }
216 }
217 return new GraphQLScalarType(config);
218 }
219 },
220 [MapperKind.ENUM_TYPE]: type => {
221 var _a, _b, _c, _d, _e, _f;
222 const resolverValue = resolvers[type.name];
223 const config = type.toConfig();
224 const enumValueConfigMap = config.values;
225 if (resolverValue != null) {
226 for (const fieldName in resolverValue) {
227 if (fieldName.startsWith('__')) {
228 config[fieldName.substring(2)] = resolverValue[fieldName];
229 }
230 else if (fieldName === 'astNode' && config.astNode != null) {
231 config.astNode = {
232 ...config.astNode,
233 description: (_b = (_a = resolverValue === null || resolverValue === void 0 ? void 0 : resolverValue.astNode) === null || _a === void 0 ? void 0 : _a.description) !== null && _b !== void 0 ? _b : config.astNode.description,
234 directives: ((_c = config.astNode.directives) !== null && _c !== void 0 ? _c : []).concat((_e = (_d = resolverValue === null || resolverValue === void 0 ? void 0 : resolverValue.astNode) === null || _d === void 0 ? void 0 : _d.directives) !== null && _e !== void 0 ? _e : []),
235 };
236 }
237 else if (fieldName === 'extensionASTNodes' && config.extensionASTNodes != null) {
238 config.extensionASTNodes = config.extensionASTNodes.concat((_f = resolverValue === null || resolverValue === void 0 ? void 0 : resolverValue.extensionASTNodes) !== null && _f !== void 0 ? _f : []);
239 }
240 else if (fieldName === 'extensions' &&
241 config.extensions != null &&
242 resolverValue.extensions != null) {
243 config.extensions = Object.assign(Object.create(null), type.extensions, resolverValue.extensions);
244 }
245 else if (enumValueConfigMap[fieldName]) {
246 enumValueConfigMap[fieldName].value = resolverValue[fieldName];
247 }
248 }
249 return new GraphQLEnumType(config);
250 }
251 },
252 [MapperKind.UNION_TYPE]: type => {
253 const resolverValue = resolvers[type.name];
254 if (resolverValue != null) {
255 const config = type.toConfig();
256 if (resolverValue['__resolveType']) {
257 config.resolveType = resolverValue['__resolveType'];
258 }
259 return new GraphQLUnionType(config);
260 }
261 },
262 [MapperKind.OBJECT_TYPE]: type => {
263 const resolverValue = resolvers[type.name];
264 if (resolverValue != null) {
265 const config = type.toConfig();
266 if (resolverValue['__isTypeOf']) {
267 config.isTypeOf = resolverValue['__isTypeOf'];
268 }
269 return new GraphQLObjectType(config);
270 }
271 },
272 [MapperKind.INTERFACE_TYPE]: type => {
273 const resolverValue = resolvers[type.name];
274 if (resolverValue != null) {
275 const config = type.toConfig();
276 if (resolverValue['__resolveType']) {
277 config.resolveType = resolverValue['__resolveType'];
278 }
279 return new GraphQLInterfaceType(config);
280 }
281 },
282 [MapperKind.COMPOSITE_FIELD]: (fieldConfig, fieldName, typeName) => {
283 const resolverValue = resolvers[typeName];
284 if (resolverValue != null) {
285 const fieldResolve = resolverValue[fieldName];
286 if (fieldResolve != null) {
287 const newFieldConfig = { ...fieldConfig };
288 if (typeof fieldResolve === 'function') {
289 // for convenience. Allows shorter syntax in resolver definition file
290 newFieldConfig.resolve = fieldResolve.bind(resolverValue);
291 }
292 else {
293 setFieldProperties(newFieldConfig, fieldResolve);
294 }
295 return newFieldConfig;
296 }
297 }
298 },
299 });
300 if (defaultFieldResolver != null) {
301 schema = mapSchema(schema, {
302 [MapperKind.OBJECT_FIELD]: fieldConfig => ({
303 ...fieldConfig,
304 resolve: fieldConfig.resolve != null ? fieldConfig.resolve : defaultFieldResolver,
305 }),
306 });
307 }
308 return schema;
309}
310function setFieldProperties(field, propertiesObj) {
311 for (const propertyName in propertiesObj) {
312 field[propertyName] = propertiesObj[propertyName];
313 }
314}