graphql-custom-fields.ts 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. import { buildSchema, extendSchema, GraphQLSchema, parse } from 'graphql';
  2. import { CustomFieldConfig, CustomFields, CustomFieldType } from '@vendure/common/shared-types';
  3. import { assertNever } from '@vendure/common/shared-utils';
  4. /**
  5. * Given a CustomFields config object, generates an SDL string extending the built-in
  6. * types with a customFields property for all entities, translations and inputs for which
  7. * custom fields are defined.
  8. */
  9. export function addGraphQLCustomFields(
  10. typeDefsOrSchema: string | GraphQLSchema,
  11. customFieldConfig: CustomFields,
  12. ): GraphQLSchema {
  13. const schema = typeof typeDefsOrSchema === 'string' ? buildSchema(typeDefsOrSchema) : typeDefsOrSchema;
  14. let customFieldTypeDefs = '';
  15. if (!schema.getType('JSON')) {
  16. customFieldTypeDefs += `
  17. scalar JSON
  18. `;
  19. }
  20. for (const entityName of Object.keys(customFieldConfig)) {
  21. const customEntityFields = customFieldConfig[entityName as keyof CustomFields] || [];
  22. const localeStringFields = customEntityFields.filter(field => field.type === 'localeString');
  23. const nonLocaleStringFields = customEntityFields.filter(field => field.type !== 'localeString');
  24. if (schema.getType(entityName)) {
  25. if (customEntityFields.length) {
  26. customFieldTypeDefs += `
  27. type ${entityName}CustomFields {
  28. ${mapToFields(customEntityFields, getGraphQlType)}
  29. }
  30. extend type ${entityName} {
  31. customFields: ${entityName}CustomFields
  32. }
  33. `;
  34. } else {
  35. customFieldTypeDefs += `
  36. extend type ${entityName} {
  37. customFields: JSON
  38. }
  39. `;
  40. }
  41. }
  42. if (localeStringFields.length && schema.getType(`${entityName}Translation`)) {
  43. customFieldTypeDefs += `
  44. type ${entityName}TranslationCustomFields {
  45. ${mapToFields(localeStringFields, getGraphQlType)}
  46. }
  47. extend type ${entityName}Translation {
  48. customFields: ${entityName}TranslationCustomFields
  49. }
  50. `;
  51. }
  52. if (schema.getType(`Create${entityName}Input`)) {
  53. if (nonLocaleStringFields.length) {
  54. customFieldTypeDefs += `
  55. input Create${entityName}CustomFieldsInput {
  56. ${mapToFields(nonLocaleStringFields, getGraphQlType)}
  57. }
  58. extend input Create${entityName}Input {
  59. customFields: Create${entityName}CustomFieldsInput
  60. }
  61. `;
  62. } else {
  63. customFieldTypeDefs += `
  64. extend input Create${entityName}Input {
  65. customFields: JSON
  66. }
  67. `;
  68. }
  69. }
  70. if (schema.getType(`Update${entityName}Input`)) {
  71. if (nonLocaleStringFields.length) {
  72. customFieldTypeDefs += `
  73. input Update${entityName}CustomFieldsInput {
  74. ${mapToFields(nonLocaleStringFields, getGraphQlType)}
  75. }
  76. extend input Update${entityName}Input {
  77. customFields: Update${entityName}CustomFieldsInput
  78. }
  79. `;
  80. } else {
  81. customFieldTypeDefs += `
  82. extend input Update${entityName}Input {
  83. customFields: JSON
  84. }
  85. `;
  86. }
  87. }
  88. if (customEntityFields.length && schema.getType(`${entityName}SortParameter`)) {
  89. customFieldTypeDefs += `
  90. extend input ${entityName}SortParameter {
  91. ${mapToFields(customEntityFields, () => 'SortOrder')}
  92. }
  93. `;
  94. }
  95. if (customEntityFields.length && schema.getType(`${entityName}FilterParameter`)) {
  96. customFieldTypeDefs += `
  97. extend input ${entityName}FilterParameter {
  98. ${mapToFields(customEntityFields, getFilterOperator)}
  99. }
  100. `;
  101. }
  102. if (localeStringFields && schema.getType(`${entityName}TranslationInput`)) {
  103. if (localeStringFields.length) {
  104. customFieldTypeDefs += `
  105. input ${entityName}TranslationCustomFieldsInput {
  106. ${mapToFields(localeStringFields, getGraphQlType)}
  107. }
  108. extend input ${entityName}TranslationInput {
  109. customFields: ${entityName}TranslationCustomFieldsInput
  110. }
  111. `;
  112. } else {
  113. customFieldTypeDefs += `
  114. extend input ${entityName}TranslationInput {
  115. customFields: JSON
  116. }
  117. `;
  118. }
  119. }
  120. }
  121. return extendSchema(schema, parse(customFieldTypeDefs));
  122. }
  123. type GraphQLFieldType = 'String' | 'Int' | 'Float' | 'Boolean' | 'ID';
  124. /**
  125. * Maps an array of CustomFieldConfig objects into a string of SDL fields.
  126. */
  127. function mapToFields(fieldDefs: CustomFieldConfig[], typeFn: (fieldType: CustomFieldType) => string): string {
  128. return fieldDefs.map(field => `${field.name}: ${typeFn(field.type)}`).join('\n');
  129. }
  130. function getFilterOperator(type: CustomFieldType): string {
  131. switch (type) {
  132. case 'datetime':
  133. return 'DateOperators';
  134. case 'string':
  135. case 'localeString':
  136. return 'StringOperators';
  137. case 'boolean':
  138. return 'BooleanOperators';
  139. case 'int':
  140. case 'float':
  141. return 'NumberOperators';
  142. default:
  143. assertNever(type);
  144. }
  145. return 'String';
  146. }
  147. function getGraphQlType(type: CustomFieldType): GraphQLFieldType {
  148. switch (type) {
  149. case 'string':
  150. case 'datetime':
  151. case 'localeString':
  152. return 'String';
  153. case 'boolean':
  154. return 'Boolean';
  155. case 'int':
  156. return 'Int';
  157. case 'float':
  158. return 'Float';
  159. default:
  160. assertNever(type);
  161. }
  162. return 'String';
  163. }