UNPKG

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