UNPKG

19.5 kBJavaScriptView Raw
1import { GraphQLEnumType, GraphQLInputObjectType, GraphQLInterfaceType, GraphQLList, GraphQLNonNull, GraphQLObjectType, GraphQLSchema, isEnumType, isInputObjectType, isInterfaceType, isLeafType, isListType, isNamedType, isNonNullType, isObjectType, isScalarType, isUnionType, Kind, } from 'graphql';
2import { getObjectTypeFromTypeMap } from './getObjectTypeFromTypeMap.js';
3import { MapperKind, } from './Interfaces.js';
4import { rewireTypes } from './rewire.js';
5import { parseInputValue, serializeInputValue } from './transformInputValue.js';
6export function mapSchema(schema, schemaMapper = {}) {
7 const newTypeMap = mapArguments(mapFields(mapTypes(mapDefaultValues(mapEnumValues(mapTypes(mapDefaultValues(schema.getTypeMap(), schema, serializeInputValue), schema, schemaMapper, type => isLeafType(type)), schema, schemaMapper), schema, parseInputValue), schema, schemaMapper, type => !isLeafType(type)), schema, schemaMapper), schema, schemaMapper);
8 const originalDirectives = schema.getDirectives();
9 const newDirectives = mapDirectives(originalDirectives, schema, schemaMapper);
10 const { typeMap, directives } = rewireTypes(newTypeMap, newDirectives);
11 return new GraphQLSchema({
12 ...schema.toConfig(),
13 query: getObjectTypeFromTypeMap(typeMap, getObjectTypeFromTypeMap(newTypeMap, schema.getQueryType())),
14 mutation: getObjectTypeFromTypeMap(typeMap, getObjectTypeFromTypeMap(newTypeMap, schema.getMutationType())),
15 subscription: getObjectTypeFromTypeMap(typeMap, getObjectTypeFromTypeMap(newTypeMap, schema.getSubscriptionType())),
16 types: Object.values(typeMap),
17 directives,
18 });
19}
20function mapTypes(originalTypeMap, schema, schemaMapper, testFn = () => true) {
21 const newTypeMap = {};
22 for (const typeName in originalTypeMap) {
23 if (!typeName.startsWith('__')) {
24 const originalType = originalTypeMap[typeName];
25 if (originalType == null || !testFn(originalType)) {
26 newTypeMap[typeName] = originalType;
27 continue;
28 }
29 const typeMapper = getTypeMapper(schema, schemaMapper, typeName);
30 if (typeMapper == null) {
31 newTypeMap[typeName] = originalType;
32 continue;
33 }
34 const maybeNewType = typeMapper(originalType, schema);
35 if (maybeNewType === undefined) {
36 newTypeMap[typeName] = originalType;
37 continue;
38 }
39 newTypeMap[typeName] = maybeNewType;
40 }
41 }
42 return newTypeMap;
43}
44function mapEnumValues(originalTypeMap, schema, schemaMapper) {
45 const enumValueMapper = getEnumValueMapper(schemaMapper);
46 if (!enumValueMapper) {
47 return originalTypeMap;
48 }
49 return mapTypes(originalTypeMap, schema, {
50 [MapperKind.ENUM_TYPE]: type => {
51 const config = type.toConfig();
52 const originalEnumValueConfigMap = config.values;
53 const newEnumValueConfigMap = {};
54 for (const externalValue in originalEnumValueConfigMap) {
55 const originalEnumValueConfig = originalEnumValueConfigMap[externalValue];
56 const mappedEnumValue = enumValueMapper(originalEnumValueConfig, type.name, schema, externalValue);
57 if (mappedEnumValue === undefined) {
58 newEnumValueConfigMap[externalValue] = originalEnumValueConfig;
59 }
60 else if (Array.isArray(mappedEnumValue)) {
61 const [newExternalValue, newEnumValueConfig] = mappedEnumValue;
62 newEnumValueConfigMap[newExternalValue] =
63 newEnumValueConfig === undefined ? originalEnumValueConfig : newEnumValueConfig;
64 }
65 else if (mappedEnumValue !== null) {
66 newEnumValueConfigMap[externalValue] = mappedEnumValue;
67 }
68 }
69 return correctASTNodes(new GraphQLEnumType({
70 ...config,
71 values: newEnumValueConfigMap,
72 }));
73 },
74 }, type => isEnumType(type));
75}
76function mapDefaultValues(originalTypeMap, schema, fn) {
77 const newTypeMap = mapArguments(originalTypeMap, schema, {
78 [MapperKind.ARGUMENT]: argumentConfig => {
79 if (argumentConfig.defaultValue === undefined) {
80 return argumentConfig;
81 }
82 const maybeNewType = getNewType(originalTypeMap, argumentConfig.type);
83 if (maybeNewType != null) {
84 return {
85 ...argumentConfig,
86 defaultValue: fn(maybeNewType, argumentConfig.defaultValue),
87 };
88 }
89 },
90 });
91 return mapFields(newTypeMap, schema, {
92 [MapperKind.INPUT_OBJECT_FIELD]: inputFieldConfig => {
93 if (inputFieldConfig.defaultValue === undefined) {
94 return inputFieldConfig;
95 }
96 const maybeNewType = getNewType(newTypeMap, inputFieldConfig.type);
97 if (maybeNewType != null) {
98 return {
99 ...inputFieldConfig,
100 defaultValue: fn(maybeNewType, inputFieldConfig.defaultValue),
101 };
102 }
103 },
104 });
105}
106function getNewType(newTypeMap, type) {
107 if (isListType(type)) {
108 const newType = getNewType(newTypeMap, type.ofType);
109 return newType != null ? new GraphQLList(newType) : null;
110 }
111 else if (isNonNullType(type)) {
112 const newType = getNewType(newTypeMap, type.ofType);
113 return newType != null ? new GraphQLNonNull(newType) : null;
114 }
115 else if (isNamedType(type)) {
116 const newType = newTypeMap[type.name];
117 return newType != null ? newType : null;
118 }
119 return null;
120}
121function mapFields(originalTypeMap, schema, schemaMapper) {
122 const newTypeMap = {};
123 for (const typeName in originalTypeMap) {
124 if (!typeName.startsWith('__')) {
125 const originalType = originalTypeMap[typeName];
126 if (!isObjectType(originalType) &&
127 !isInterfaceType(originalType) &&
128 !isInputObjectType(originalType)) {
129 newTypeMap[typeName] = originalType;
130 continue;
131 }
132 const fieldMapper = getFieldMapper(schema, schemaMapper, typeName);
133 if (fieldMapper == null) {
134 newTypeMap[typeName] = originalType;
135 continue;
136 }
137 const config = originalType.toConfig();
138 const originalFieldConfigMap = config.fields;
139 const newFieldConfigMap = {};
140 for (const fieldName in originalFieldConfigMap) {
141 const originalFieldConfig = originalFieldConfigMap[fieldName];
142 const mappedField = fieldMapper(originalFieldConfig, fieldName, typeName, schema);
143 if (mappedField === undefined) {
144 newFieldConfigMap[fieldName] = originalFieldConfig;
145 }
146 else if (Array.isArray(mappedField)) {
147 const [newFieldName, newFieldConfig] = mappedField;
148 if (newFieldConfig.astNode != null) {
149 newFieldConfig.astNode = {
150 ...newFieldConfig.astNode,
151 name: {
152 ...newFieldConfig.astNode.name,
153 value: newFieldName,
154 },
155 };
156 }
157 newFieldConfigMap[newFieldName] =
158 newFieldConfig === undefined ? originalFieldConfig : newFieldConfig;
159 }
160 else if (mappedField !== null) {
161 newFieldConfigMap[fieldName] = mappedField;
162 }
163 }
164 if (isObjectType(originalType)) {
165 newTypeMap[typeName] = correctASTNodes(new GraphQLObjectType({
166 ...config,
167 fields: newFieldConfigMap,
168 }));
169 }
170 else if (isInterfaceType(originalType)) {
171 newTypeMap[typeName] = correctASTNodes(new GraphQLInterfaceType({
172 ...config,
173 fields: newFieldConfigMap,
174 }));
175 }
176 else {
177 newTypeMap[typeName] = correctASTNodes(new GraphQLInputObjectType({
178 ...config,
179 fields: newFieldConfigMap,
180 }));
181 }
182 }
183 }
184 return newTypeMap;
185}
186function mapArguments(originalTypeMap, schema, schemaMapper) {
187 const newTypeMap = {};
188 for (const typeName in originalTypeMap) {
189 if (!typeName.startsWith('__')) {
190 const originalType = originalTypeMap[typeName];
191 if (!isObjectType(originalType) && !isInterfaceType(originalType)) {
192 newTypeMap[typeName] = originalType;
193 continue;
194 }
195 const argumentMapper = getArgumentMapper(schemaMapper);
196 if (argumentMapper == null) {
197 newTypeMap[typeName] = originalType;
198 continue;
199 }
200 const config = originalType.toConfig();
201 const originalFieldConfigMap = config.fields;
202 const newFieldConfigMap = {};
203 for (const fieldName in originalFieldConfigMap) {
204 const originalFieldConfig = originalFieldConfigMap[fieldName];
205 const originalArgumentConfigMap = originalFieldConfig.args;
206 if (originalArgumentConfigMap == null) {
207 newFieldConfigMap[fieldName] = originalFieldConfig;
208 continue;
209 }
210 const argumentNames = Object.keys(originalArgumentConfigMap);
211 if (!argumentNames.length) {
212 newFieldConfigMap[fieldName] = originalFieldConfig;
213 continue;
214 }
215 const newArgumentConfigMap = {};
216 for (const argumentName of argumentNames) {
217 const originalArgumentConfig = originalArgumentConfigMap[argumentName];
218 const mappedArgument = argumentMapper(originalArgumentConfig, fieldName, typeName, schema);
219 if (mappedArgument === undefined) {
220 newArgumentConfigMap[argumentName] = originalArgumentConfig;
221 }
222 else if (Array.isArray(mappedArgument)) {
223 const [newArgumentName, newArgumentConfig] = mappedArgument;
224 newArgumentConfigMap[newArgumentName] = newArgumentConfig;
225 }
226 else if (mappedArgument !== null) {
227 newArgumentConfigMap[argumentName] = mappedArgument;
228 }
229 }
230 newFieldConfigMap[fieldName] = {
231 ...originalFieldConfig,
232 args: newArgumentConfigMap,
233 };
234 }
235 if (isObjectType(originalType)) {
236 newTypeMap[typeName] = new GraphQLObjectType({
237 ...config,
238 fields: newFieldConfigMap,
239 });
240 }
241 else if (isInterfaceType(originalType)) {
242 newTypeMap[typeName] = new GraphQLInterfaceType({
243 ...config,
244 fields: newFieldConfigMap,
245 });
246 }
247 else {
248 newTypeMap[typeName] = new GraphQLInputObjectType({
249 ...config,
250 fields: newFieldConfigMap,
251 });
252 }
253 }
254 }
255 return newTypeMap;
256}
257function mapDirectives(originalDirectives, schema, schemaMapper) {
258 const directiveMapper = getDirectiveMapper(schemaMapper);
259 if (directiveMapper == null) {
260 return originalDirectives.slice();
261 }
262 const newDirectives = [];
263 for (const directive of originalDirectives) {
264 const mappedDirective = directiveMapper(directive, schema);
265 if (mappedDirective === undefined) {
266 newDirectives.push(directive);
267 }
268 else if (mappedDirective !== null) {
269 newDirectives.push(mappedDirective);
270 }
271 }
272 return newDirectives;
273}
274function getTypeSpecifiers(schema, typeName) {
275 const type = schema.getType(typeName);
276 const specifiers = [MapperKind.TYPE];
277 if (isObjectType(type)) {
278 specifiers.push(MapperKind.COMPOSITE_TYPE, MapperKind.OBJECT_TYPE);
279 if (typeName === schema.getQueryType()?.name) {
280 specifiers.push(MapperKind.ROOT_OBJECT, MapperKind.QUERY);
281 }
282 else if (typeName === schema.getMutationType()?.name) {
283 specifiers.push(MapperKind.ROOT_OBJECT, MapperKind.MUTATION);
284 }
285 else if (typeName === schema.getSubscriptionType()?.name) {
286 specifiers.push(MapperKind.ROOT_OBJECT, MapperKind.SUBSCRIPTION);
287 }
288 }
289 else if (isInputObjectType(type)) {
290 specifiers.push(MapperKind.INPUT_OBJECT_TYPE);
291 }
292 else if (isInterfaceType(type)) {
293 specifiers.push(MapperKind.COMPOSITE_TYPE, MapperKind.ABSTRACT_TYPE, MapperKind.INTERFACE_TYPE);
294 }
295 else if (isUnionType(type)) {
296 specifiers.push(MapperKind.COMPOSITE_TYPE, MapperKind.ABSTRACT_TYPE, MapperKind.UNION_TYPE);
297 }
298 else if (isEnumType(type)) {
299 specifiers.push(MapperKind.ENUM_TYPE);
300 }
301 else if (isScalarType(type)) {
302 specifiers.push(MapperKind.SCALAR_TYPE);
303 }
304 return specifiers;
305}
306function getTypeMapper(schema, schemaMapper, typeName) {
307 const specifiers = getTypeSpecifiers(schema, typeName);
308 let typeMapper;
309 const stack = [...specifiers];
310 while (!typeMapper && stack.length > 0) {
311 // It is safe to use the ! operator here as we check the length.
312 const next = stack.pop();
313 typeMapper = schemaMapper[next];
314 }
315 return typeMapper != null ? typeMapper : null;
316}
317function getFieldSpecifiers(schema, typeName) {
318 const type = schema.getType(typeName);
319 const specifiers = [MapperKind.FIELD];
320 if (isObjectType(type)) {
321 specifiers.push(MapperKind.COMPOSITE_FIELD, MapperKind.OBJECT_FIELD);
322 if (typeName === schema.getQueryType()?.name) {
323 specifiers.push(MapperKind.ROOT_FIELD, MapperKind.QUERY_ROOT_FIELD);
324 }
325 else if (typeName === schema.getMutationType()?.name) {
326 specifiers.push(MapperKind.ROOT_FIELD, MapperKind.MUTATION_ROOT_FIELD);
327 }
328 else if (typeName === schema.getSubscriptionType()?.name) {
329 specifiers.push(MapperKind.ROOT_FIELD, MapperKind.SUBSCRIPTION_ROOT_FIELD);
330 }
331 }
332 else if (isInterfaceType(type)) {
333 specifiers.push(MapperKind.COMPOSITE_FIELD, MapperKind.INTERFACE_FIELD);
334 }
335 else if (isInputObjectType(type)) {
336 specifiers.push(MapperKind.INPUT_OBJECT_FIELD);
337 }
338 return specifiers;
339}
340function getFieldMapper(schema, schemaMapper, typeName) {
341 const specifiers = getFieldSpecifiers(schema, typeName);
342 let fieldMapper;
343 const stack = [...specifiers];
344 while (!fieldMapper && stack.length > 0) {
345 // It is safe to use the ! operator here as we check the length.
346 const next = stack.pop();
347 // TODO: fix this as unknown cast
348 fieldMapper = schemaMapper[next];
349 }
350 return fieldMapper ?? null;
351}
352function getArgumentMapper(schemaMapper) {
353 const argumentMapper = schemaMapper[MapperKind.ARGUMENT];
354 return argumentMapper != null ? argumentMapper : null;
355}
356function getDirectiveMapper(schemaMapper) {
357 const directiveMapper = schemaMapper[MapperKind.DIRECTIVE];
358 return directiveMapper != null ? directiveMapper : null;
359}
360function getEnumValueMapper(schemaMapper) {
361 const enumValueMapper = schemaMapper[MapperKind.ENUM_VALUE];
362 return enumValueMapper != null ? enumValueMapper : null;
363}
364export function correctASTNodes(type) {
365 if (isObjectType(type)) {
366 const config = type.toConfig();
367 if (config.astNode != null) {
368 const fields = [];
369 for (const fieldName in config.fields) {
370 const fieldConfig = config.fields[fieldName];
371 if (fieldConfig.astNode != null) {
372 fields.push(fieldConfig.astNode);
373 }
374 }
375 config.astNode = {
376 ...config.astNode,
377 kind: Kind.OBJECT_TYPE_DEFINITION,
378 fields,
379 };
380 }
381 if (config.extensionASTNodes != null) {
382 config.extensionASTNodes = config.extensionASTNodes.map(node => ({
383 ...node,
384 kind: Kind.OBJECT_TYPE_EXTENSION,
385 fields: undefined,
386 }));
387 }
388 return new GraphQLObjectType(config);
389 }
390 else if (isInterfaceType(type)) {
391 const config = type.toConfig();
392 if (config.astNode != null) {
393 const fields = [];
394 for (const fieldName in config.fields) {
395 const fieldConfig = config.fields[fieldName];
396 if (fieldConfig.astNode != null) {
397 fields.push(fieldConfig.astNode);
398 }
399 }
400 config.astNode = {
401 ...config.astNode,
402 kind: Kind.INTERFACE_TYPE_DEFINITION,
403 fields,
404 };
405 }
406 if (config.extensionASTNodes != null) {
407 config.extensionASTNodes = config.extensionASTNodes.map(node => ({
408 ...node,
409 kind: Kind.INTERFACE_TYPE_EXTENSION,
410 fields: undefined,
411 }));
412 }
413 return new GraphQLInterfaceType(config);
414 }
415 else if (isInputObjectType(type)) {
416 const config = type.toConfig();
417 if (config.astNode != null) {
418 const fields = [];
419 for (const fieldName in config.fields) {
420 const fieldConfig = config.fields[fieldName];
421 if (fieldConfig.astNode != null) {
422 fields.push(fieldConfig.astNode);
423 }
424 }
425 config.astNode = {
426 ...config.astNode,
427 kind: Kind.INPUT_OBJECT_TYPE_DEFINITION,
428 fields,
429 };
430 }
431 if (config.extensionASTNodes != null) {
432 config.extensionASTNodes = config.extensionASTNodes.map(node => ({
433 ...node,
434 kind: Kind.INPUT_OBJECT_TYPE_EXTENSION,
435 fields: undefined,
436 }));
437 }
438 return new GraphQLInputObjectType(config);
439 }
440 else if (isEnumType(type)) {
441 const config = type.toConfig();
442 if (config.astNode != null) {
443 const values = [];
444 for (const enumKey in config.values) {
445 const enumValueConfig = config.values[enumKey];
446 if (enumValueConfig.astNode != null) {
447 values.push(enumValueConfig.astNode);
448 }
449 }
450 config.astNode = {
451 ...config.astNode,
452 values,
453 };
454 }
455 if (config.extensionASTNodes != null) {
456 config.extensionASTNodes = config.extensionASTNodes.map(node => ({
457 ...node,
458 values: undefined,
459 }));
460 }
461 return new GraphQLEnumType(config);
462 }
463 else {
464 return type;
465 }
466}