mapSchema.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  1. import { GraphQLObjectType, GraphQLSchema, isInterfaceType, isEnumType, isObjectType, isScalarType, isUnionType, isInputObjectType, GraphQLInputObjectType, GraphQLInterfaceType, isLeafType, isListType, isNonNullType, isNamedType, GraphQLList, GraphQLNonNull, GraphQLEnumType, Kind, } from 'graphql';
  2. import { getObjectTypeFromTypeMap } from './getObjectTypeFromTypeMap.js';
  3. import { MapperKind, } from './Interfaces.js';
  4. import { rewireTypes } from './rewire.js';
  5. import { serializeInputValue, parseInputValue } from './transformInputValue.js';
  6. export 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. }
  20. function 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. }
  44. function 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. }
  76. function 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. }
  106. function 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. }
  121. function 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) && !isInterfaceType(originalType) && !isInputObjectType(originalType)) {
  127. newTypeMap[typeName] = originalType;
  128. continue;
  129. }
  130. const fieldMapper = getFieldMapper(schema, schemaMapper, typeName);
  131. if (fieldMapper == null) {
  132. newTypeMap[typeName] = originalType;
  133. continue;
  134. }
  135. const config = originalType.toConfig();
  136. const originalFieldConfigMap = config.fields;
  137. const newFieldConfigMap = {};
  138. for (const fieldName in originalFieldConfigMap) {
  139. const originalFieldConfig = originalFieldConfigMap[fieldName];
  140. const mappedField = fieldMapper(originalFieldConfig, fieldName, typeName, schema);
  141. if (mappedField === undefined) {
  142. newFieldConfigMap[fieldName] = originalFieldConfig;
  143. }
  144. else if (Array.isArray(mappedField)) {
  145. const [newFieldName, newFieldConfig] = mappedField;
  146. if (newFieldConfig.astNode != null) {
  147. newFieldConfig.astNode = {
  148. ...newFieldConfig.astNode,
  149. name: {
  150. ...newFieldConfig.astNode.name,
  151. value: newFieldName,
  152. },
  153. };
  154. }
  155. newFieldConfigMap[newFieldName] = newFieldConfig === undefined ? originalFieldConfig : newFieldConfig;
  156. }
  157. else if (mappedField !== null) {
  158. newFieldConfigMap[fieldName] = mappedField;
  159. }
  160. }
  161. if (isObjectType(originalType)) {
  162. newTypeMap[typeName] = correctASTNodes(new GraphQLObjectType({
  163. ...config,
  164. fields: newFieldConfigMap,
  165. }));
  166. }
  167. else if (isInterfaceType(originalType)) {
  168. newTypeMap[typeName] = correctASTNodes(new GraphQLInterfaceType({
  169. ...config,
  170. fields: newFieldConfigMap,
  171. }));
  172. }
  173. else {
  174. newTypeMap[typeName] = correctASTNodes(new GraphQLInputObjectType({
  175. ...config,
  176. fields: newFieldConfigMap,
  177. }));
  178. }
  179. }
  180. }
  181. return newTypeMap;
  182. }
  183. function mapArguments(originalTypeMap, schema, schemaMapper) {
  184. const newTypeMap = {};
  185. for (const typeName in originalTypeMap) {
  186. if (!typeName.startsWith('__')) {
  187. const originalType = originalTypeMap[typeName];
  188. if (!isObjectType(originalType) && !isInterfaceType(originalType)) {
  189. newTypeMap[typeName] = originalType;
  190. continue;
  191. }
  192. const argumentMapper = getArgumentMapper(schemaMapper);
  193. if (argumentMapper == null) {
  194. newTypeMap[typeName] = originalType;
  195. continue;
  196. }
  197. const config = originalType.toConfig();
  198. const originalFieldConfigMap = config.fields;
  199. const newFieldConfigMap = {};
  200. for (const fieldName in originalFieldConfigMap) {
  201. const originalFieldConfig = originalFieldConfigMap[fieldName];
  202. const originalArgumentConfigMap = originalFieldConfig.args;
  203. if (originalArgumentConfigMap == null) {
  204. newFieldConfigMap[fieldName] = originalFieldConfig;
  205. continue;
  206. }
  207. const argumentNames = Object.keys(originalArgumentConfigMap);
  208. if (!argumentNames.length) {
  209. newFieldConfigMap[fieldName] = originalFieldConfig;
  210. continue;
  211. }
  212. const newArgumentConfigMap = {};
  213. for (const argumentName of argumentNames) {
  214. const originalArgumentConfig = originalArgumentConfigMap[argumentName];
  215. const mappedArgument = argumentMapper(originalArgumentConfig, fieldName, typeName, schema);
  216. if (mappedArgument === undefined) {
  217. newArgumentConfigMap[argumentName] = originalArgumentConfig;
  218. }
  219. else if (Array.isArray(mappedArgument)) {
  220. const [newArgumentName, newArgumentConfig] = mappedArgument;
  221. newArgumentConfigMap[newArgumentName] = newArgumentConfig;
  222. }
  223. else if (mappedArgument !== null) {
  224. newArgumentConfigMap[argumentName] = mappedArgument;
  225. }
  226. }
  227. newFieldConfigMap[fieldName] = {
  228. ...originalFieldConfig,
  229. args: newArgumentConfigMap,
  230. };
  231. }
  232. if (isObjectType(originalType)) {
  233. newTypeMap[typeName] = new GraphQLObjectType({
  234. ...config,
  235. fields: newFieldConfigMap,
  236. });
  237. }
  238. else if (isInterfaceType(originalType)) {
  239. newTypeMap[typeName] = new GraphQLInterfaceType({
  240. ...config,
  241. fields: newFieldConfigMap,
  242. });
  243. }
  244. else {
  245. newTypeMap[typeName] = new GraphQLInputObjectType({
  246. ...config,
  247. fields: newFieldConfigMap,
  248. });
  249. }
  250. }
  251. }
  252. return newTypeMap;
  253. }
  254. function mapDirectives(originalDirectives, schema, schemaMapper) {
  255. const directiveMapper = getDirectiveMapper(schemaMapper);
  256. if (directiveMapper == null) {
  257. return originalDirectives.slice();
  258. }
  259. const newDirectives = [];
  260. for (const directive of originalDirectives) {
  261. const mappedDirective = directiveMapper(directive, schema);
  262. if (mappedDirective === undefined) {
  263. newDirectives.push(directive);
  264. }
  265. else if (mappedDirective !== null) {
  266. newDirectives.push(mappedDirective);
  267. }
  268. }
  269. return newDirectives;
  270. }
  271. function getTypeSpecifiers(schema, typeName) {
  272. var _a, _b, _c;
  273. const type = schema.getType(typeName);
  274. const specifiers = [MapperKind.TYPE];
  275. if (isObjectType(type)) {
  276. specifiers.push(MapperKind.COMPOSITE_TYPE, MapperKind.OBJECT_TYPE);
  277. if (typeName === ((_a = schema.getQueryType()) === null || _a === void 0 ? void 0 : _a.name)) {
  278. specifiers.push(MapperKind.ROOT_OBJECT, MapperKind.QUERY);
  279. }
  280. else if (typeName === ((_b = schema.getMutationType()) === null || _b === void 0 ? void 0 : _b.name)) {
  281. specifiers.push(MapperKind.ROOT_OBJECT, MapperKind.MUTATION);
  282. }
  283. else if (typeName === ((_c = schema.getSubscriptionType()) === null || _c === void 0 ? void 0 : _c.name)) {
  284. specifiers.push(MapperKind.ROOT_OBJECT, MapperKind.SUBSCRIPTION);
  285. }
  286. }
  287. else if (isInputObjectType(type)) {
  288. specifiers.push(MapperKind.INPUT_OBJECT_TYPE);
  289. }
  290. else if (isInterfaceType(type)) {
  291. specifiers.push(MapperKind.COMPOSITE_TYPE, MapperKind.ABSTRACT_TYPE, MapperKind.INTERFACE_TYPE);
  292. }
  293. else if (isUnionType(type)) {
  294. specifiers.push(MapperKind.COMPOSITE_TYPE, MapperKind.ABSTRACT_TYPE, MapperKind.UNION_TYPE);
  295. }
  296. else if (isEnumType(type)) {
  297. specifiers.push(MapperKind.ENUM_TYPE);
  298. }
  299. else if (isScalarType(type)) {
  300. specifiers.push(MapperKind.SCALAR_TYPE);
  301. }
  302. return specifiers;
  303. }
  304. function getTypeMapper(schema, schemaMapper, typeName) {
  305. const specifiers = getTypeSpecifiers(schema, typeName);
  306. let typeMapper;
  307. const stack = [...specifiers];
  308. while (!typeMapper && stack.length > 0) {
  309. // It is safe to use the ! operator here as we check the length.
  310. const next = stack.pop();
  311. typeMapper = schemaMapper[next];
  312. }
  313. return typeMapper != null ? typeMapper : null;
  314. }
  315. function getFieldSpecifiers(schema, typeName) {
  316. var _a, _b, _c;
  317. const type = schema.getType(typeName);
  318. const specifiers = [MapperKind.FIELD];
  319. if (isObjectType(type)) {
  320. specifiers.push(MapperKind.COMPOSITE_FIELD, MapperKind.OBJECT_FIELD);
  321. if (typeName === ((_a = schema.getQueryType()) === null || _a === void 0 ? void 0 : _a.name)) {
  322. specifiers.push(MapperKind.ROOT_FIELD, MapperKind.QUERY_ROOT_FIELD);
  323. }
  324. else if (typeName === ((_b = schema.getMutationType()) === null || _b === void 0 ? void 0 : _b.name)) {
  325. specifiers.push(MapperKind.ROOT_FIELD, MapperKind.MUTATION_ROOT_FIELD);
  326. }
  327. else if (typeName === ((_c = schema.getSubscriptionType()) === null || _c === void 0 ? void 0 : _c.name)) {
  328. specifiers.push(MapperKind.ROOT_FIELD, MapperKind.SUBSCRIPTION_ROOT_FIELD);
  329. }
  330. }
  331. else if (isInterfaceType(type)) {
  332. specifiers.push(MapperKind.COMPOSITE_FIELD, MapperKind.INTERFACE_FIELD);
  333. }
  334. else if (isInputObjectType(type)) {
  335. specifiers.push(MapperKind.INPUT_OBJECT_FIELD);
  336. }
  337. return specifiers;
  338. }
  339. function getFieldMapper(schema, schemaMapper, typeName) {
  340. const specifiers = getFieldSpecifiers(schema, typeName);
  341. let fieldMapper;
  342. const stack = [...specifiers];
  343. while (!fieldMapper && stack.length > 0) {
  344. // It is safe to use the ! operator here as we check the length.
  345. const next = stack.pop();
  346. // TODO: fix this as unknown cast
  347. fieldMapper = schemaMapper[next];
  348. }
  349. return fieldMapper !== null && fieldMapper !== void 0 ? fieldMapper : null;
  350. }
  351. function getArgumentMapper(schemaMapper) {
  352. const argumentMapper = schemaMapper[MapperKind.ARGUMENT];
  353. return argumentMapper != null ? argumentMapper : null;
  354. }
  355. function getDirectiveMapper(schemaMapper) {
  356. const directiveMapper = schemaMapper[MapperKind.DIRECTIVE];
  357. return directiveMapper != null ? directiveMapper : null;
  358. }
  359. function getEnumValueMapper(schemaMapper) {
  360. const enumValueMapper = schemaMapper[MapperKind.ENUM_VALUE];
  361. return enumValueMapper != null ? enumValueMapper : null;
  362. }
  363. export function correctASTNodes(type) {
  364. if (isObjectType(type)) {
  365. const config = type.toConfig();
  366. if (config.astNode != null) {
  367. const fields = [];
  368. for (const fieldName in config.fields) {
  369. const fieldConfig = config.fields[fieldName];
  370. if (fieldConfig.astNode != null) {
  371. fields.push(fieldConfig.astNode);
  372. }
  373. }
  374. config.astNode = {
  375. ...config.astNode,
  376. kind: Kind.OBJECT_TYPE_DEFINITION,
  377. fields,
  378. };
  379. }
  380. if (config.extensionASTNodes != null) {
  381. config.extensionASTNodes = config.extensionASTNodes.map(node => ({
  382. ...node,
  383. kind: Kind.OBJECT_TYPE_EXTENSION,
  384. fields: undefined,
  385. }));
  386. }
  387. return new GraphQLObjectType(config);
  388. }
  389. else if (isInterfaceType(type)) {
  390. const config = type.toConfig();
  391. if (config.astNode != null) {
  392. const fields = [];
  393. for (const fieldName in config.fields) {
  394. const fieldConfig = config.fields[fieldName];
  395. if (fieldConfig.astNode != null) {
  396. fields.push(fieldConfig.astNode);
  397. }
  398. }
  399. config.astNode = {
  400. ...config.astNode,
  401. kind: Kind.INTERFACE_TYPE_DEFINITION,
  402. fields,
  403. };
  404. }
  405. if (config.extensionASTNodes != null) {
  406. config.extensionASTNodes = config.extensionASTNodes.map(node => ({
  407. ...node,
  408. kind: Kind.INTERFACE_TYPE_EXTENSION,
  409. fields: undefined,
  410. }));
  411. }
  412. return new GraphQLInterfaceType(config);
  413. }
  414. else if (isInputObjectType(type)) {
  415. const config = type.toConfig();
  416. if (config.astNode != null) {
  417. const fields = [];
  418. for (const fieldName in config.fields) {
  419. const fieldConfig = config.fields[fieldName];
  420. if (fieldConfig.astNode != null) {
  421. fields.push(fieldConfig.astNode);
  422. }
  423. }
  424. config.astNode = {
  425. ...config.astNode,
  426. kind: Kind.INPUT_OBJECT_TYPE_DEFINITION,
  427. fields,
  428. };
  429. }
  430. if (config.extensionASTNodes != null) {
  431. config.extensionASTNodes = config.extensionASTNodes.map(node => ({
  432. ...node,
  433. kind: Kind.INPUT_OBJECT_TYPE_EXTENSION,
  434. fields: undefined,
  435. }));
  436. }
  437. return new GraphQLInputObjectType(config);
  438. }
  439. else if (isEnumType(type)) {
  440. const config = type.toConfig();
  441. if (config.astNode != null) {
  442. const values = [];
  443. for (const enumKey in config.values) {
  444. const enumValueConfig = config.values[enumKey];
  445. if (enumValueConfig.astNode != null) {
  446. values.push(enumValueConfig.astNode);
  447. }
  448. }
  449. config.astNode = {
  450. ...config.astNode,
  451. values,
  452. };
  453. }
  454. if (config.extensionASTNodes != null) {
  455. config.extensionASTNodes = config.extensionASTNodes.map(node => ({
  456. ...node,
  457. values: undefined,
  458. }));
  459. }
  460. return new GraphQLEnumType(config);
  461. }
  462. else {
  463. return type;
  464. }
  465. }