remove-readonly-custom-fields.ts 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. import { CustomFieldConfig } from '@vendure/common/lib/generated-types';
  2. import { DocumentNode, getOperationAST, NamedTypeNode, TypeNode } from 'graphql';
  3. import { simpleDeepClone } from 'shared/simple-deep-clone';
  4. const CREATE_ENTITY_REGEX = /Create([A-Za-z]+)Input/;
  5. const UPDATE_ENTITY_REGEX = /Update([A-Za-z]+)Input/;
  6. /**
  7. * Checks the current documentNode for an operation with a variable named "Create<Entity>Input" or "Update<Entity>Input"
  8. * and if a match is found, returns the <Entity> name.
  9. */
  10. export function isEntityCreateOrUpdateMutation(documentNode: DocumentNode): string | undefined {
  11. const operationDef = getOperationAST(documentNode, null);
  12. if (operationDef && operationDef.variableDefinitions) {
  13. for (const variableDef of operationDef.variableDefinitions) {
  14. const namedType = extractInputType(variableDef.type);
  15. const inputTypeName = namedType.name.value;
  16. const createMatch = inputTypeName.match(CREATE_ENTITY_REGEX);
  17. if (createMatch) {
  18. return createMatch[1];
  19. }
  20. const updateMatch = inputTypeName.match(UPDATE_ENTITY_REGEX);
  21. if (updateMatch) {
  22. return updateMatch[1];
  23. }
  24. }
  25. }
  26. }
  27. function extractInputType(type: TypeNode): NamedTypeNode {
  28. if (type.kind === 'NonNullType') {
  29. return extractInputType(type.type);
  30. }
  31. if (type.kind === 'ListType') {
  32. return extractInputType(type.type);
  33. }
  34. return type;
  35. }
  36. /**
  37. * Removes any `readonly` custom fields from an entity (including its translations).
  38. * To be used before submitting the entity for a create or update request.
  39. */
  40. export function removeReadonlyCustomFields<T extends any = any>(
  41. variables: T,
  42. customFieldConfig: CustomFieldConfig[],
  43. ): T {
  44. const clone = simpleDeepClone(variables as any);
  45. if (clone.input) {
  46. removeReadonly(clone.input, customFieldConfig);
  47. }
  48. return removeReadonly(clone, customFieldConfig);
  49. }
  50. function removeReadonly(input: any, customFieldConfig: CustomFieldConfig[]) {
  51. for (const field of customFieldConfig) {
  52. if (field.readonly) {
  53. if (field.type === 'localeString') {
  54. if (hasTranslations(input)) {
  55. for (const translation of input.translations) {
  56. if (
  57. hasCustomFields(translation) &&
  58. translation.customFields[field.name] !== undefined
  59. ) {
  60. delete translation.customFields[field.name];
  61. }
  62. }
  63. }
  64. } else {
  65. if (hasCustomFields(input) && input.customFields[field.name] !== undefined) {
  66. delete input.customFields[field.name];
  67. }
  68. }
  69. }
  70. }
  71. return input;
  72. }
  73. function hasCustomFields(input: any): input is { customFields: { [key: string]: any } } {
  74. return input != null && input.hasOwnProperty('customFields');
  75. }
  76. function hasTranslations(input: any): input is { translations: any[] } {
  77. return input != null && input.hasOwnProperty('translations');
  78. }