From 867422627ce2af305794f3a6def7f124a083df44 Mon Sep 17 00:00:00 2001 From: martmull Date: Fri, 14 Nov 2025 13:47:22 +0100 Subject: [PATCH] Revert "Common - Field validation (#15491)" (#15821) This reverts commit 261cba40ef612b68d0f8d558b03bfc019122b38f. --- .../common-arg-handlers.ts | 3 + .../common-args-processors.ts | 4 - .../data-arg-processor/data-arg.processor.ts | 271 -------------- .../transform-actor-field.util.spec.ts | 84 ----- .../transform-address-field.util.spec.ts | 45 --- .../transform-array-field.util.spec.ts | 27 -- .../transform-currency-field.util.spec.ts | 54 --- .../transform-full-name-field.util.spec.ts | 33 -- .../transform-numeric-field.util.spec.ts | 21 -- .../transform-raw-json-field.util.spec.ts | 21 -- .../transform-text-field.util.spec.ts | 21 -- .../transform-actor-field.util.ts | 24 -- .../transform-address-field.util.ts | 56 --- .../transform-array-field.util.ts | 14 - .../transform-currency-field.util.ts | 26 -- .../transform-full-name-field.util.ts | 25 -- .../transform-numeric-field.util.ts | 7 - .../transform-raw-json-field.util.ts | 12 - .../transform-text-field.util.ts | 8 - ...validate-actor-field-or-throw.util.spec.ts | 84 ----- ...lidate-address-field-or-throw.util.spec.ts | 159 -------- ...validate-array-field-or-throw.util.spec.ts | 55 --- ...lidate-boolean-field-or-throw.util.spec.ts | 70 ---- ...idate-currency-field-or-throw.util.spec.ts | 73 ---- ...-and-date-time-field-or-throw.util.spec.ts | 97 ----- ...alidate-emails-field-or-throw.util.spec.ts | 87 ----- ...date-full-name-field-or-throw.util.spec.ts | 70 ---- ...validate-links-field-or-throw.util.spec.ts | 88 ----- ...e-multi-select-field-or-throw.util.spec.ts | 73 ---- ...alidate-number-field-or-throw.util.spec.ts | 87 ----- ...lidate-numeric-field-or-throw.util.spec.ts | 49 --- ...alidate-phones-field-or-throw.util.spec.ts | 88 ----- ...ing-and-select-field-or-throw.util.spec.ts | 51 --- ...idate-raw-json-field-or-throw.util.spec.ts | 60 --- ...e-rich-text-v2-field-or-throw.util.spec.ts | 53 --- .../validate-text-field-or-throw.util.spec.ts | 69 ---- .../validate-uuid-field-or-throw.util.spec.ts | 39 -- .../validate-actor-field-or-throw.util.ts | 43 --- .../validate-address-field-or-throw.util.ts | 72 ---- .../validate-array-field-or-throw.util.ts | 26 -- .../validate-boolean-field-or-throw.util.ts | 21 -- .../validate-currency-field-or-throw.util.ts | 43 --- ...-date-and-date-time-field-or-throw.util.ts | 26 -- .../validate-emails-field-or-throw.util.ts | 42 --- .../validate-full-name-field-or-throw.util.ts | 41 --- .../validate-links-field-or-throw.util.ts | 39 -- ...lidate-multi-select-field-or-throw.util.ts | 41 --- .../validate-number-field-or-throw.util.ts | 25 -- .../validate-numeric-field-or-throw.util.ts | 18 - .../validate-phones-field-or-throw.util.ts | 43 --- ...e-rating-and-select-field-or-throw.util.ts | 34 -- .../validate-raw-json-field-or-throw.util.ts | 24 -- ...lidate-rich-text-v2-field-or-throw.util.ts | 53 --- .../validate-text-field-or-throw.util.ts | 21 -- .../validate-uuid-field-or-throw.util.ts | 22 -- .../query-runner-args.factory.ts | 107 ------ .../common-base-query-runner.service.ts | 18 +- ...common-create-many-query-runner.service.ts | 2 +- .../common-create-one-query-runner.service.ts | 14 +- ...on-find-duplicates-query-runner.service.ts | 2 +- .../common-merge-many-query-runner.service.ts | 8 +- ...common-update-many-query-runner.service.ts | 2 +- .../common-update-one-query-runner.service.ts | 2 +- .../errors/common-query-runner.exception.ts | 1 - ...r-to-graphql-api-exception-handler.util.ts | 1 - ...nner-to-rest-api-exception-handler.util.ts | 1 - .../api/common/core-common-api.module.ts | 8 +- .../common-selected-fields-result.type.ts | 1 + .../query-runner-args.factory.spec.ts | 224 ++++++++++++ .../workspace-query-runner/factories/index.ts | 8 + .../query-result-getters.factory.ts | 237 ++++++++++++ .../factories/query-runner-args.factory.ts | 323 +++++++++++++++++ ...nner-graphql-api-exception-handler.util.ts | 1 + .../workspace-query-runner.module.ts | 8 +- .../core/handlers/rest-api-base.handler.ts | 3 + .../parse-filter-rest-request.util.ts | 1 + .../parse-order-by-rest-request.util.ts | 6 +- ...parse-order-by-rest-request-common.util.ts | 4 +- ...-order-by-for-records-rest-request.util.ts | 4 +- .../utils/__tests__/components.utils.spec.ts | 12 +- .../utils/generate-random-field-value.util.ts | 6 +- .../record-input-transformer.service.ts | 88 ++++- .../utils/transform-emails-value.util.ts | 34 -- .../utils/transform-links-value.util.ts | 2 +- .../utils/transform-rich-text-v2.util.ts | 46 --- ...-input-validation.integration-spec.ts.snap | 5 - ...-input-validation.integration-spec.ts.snap | 4 +- ...-input-validation.integration-spec.ts.snap | 5 - ...-input-validation.integration-spec.ts.snap | 16 +- ...-input-validation.integration-spec.ts.snap | 16 +- ...-input-validation.integration-spec.ts.snap | 5 - ...-input-validation.integration-spec.ts.snap | 5 - ...-input-validation.integration-spec.ts.snap | 5 - ...-input-validation.integration-spec.ts.snap | 10 +- ...-input-validation.integration-spec.ts.snap | 5 - ...-input-validation.integration-spec.ts.snap | 10 +- ...-input-validation.integration-spec.ts.snap | 4 +- ...-input-validation.integration-spec.ts.snap | 24 +- ...-input-validation.integration-spec.ts.snap | 4 +- ...-input-validation.integration-spec.ts.snap | 5 - ...-input-validation.integration-spec.ts.snap | 10 +- ...-input-validation.integration-spec.ts.snap | 16 - ...-input-validation.integration-spec.ts.snap | 10 +- ...reate-input-validation.integration-spec.ts | 40 -- ...e-input-by-field-metadata-type.constant.ts | 342 +++++++++++------- ...e-input-by-field-metadata-type.constant.ts | 10 - ...reate-input-validation.integration-spec.ts | 40 -- ...reate-input-validation.integration-spec.ts | 40 -- ...reate-input-validation.integration-spec.ts | 40 -- ...reate-input-validation.integration-spec.ts | 40 -- ...reate-input-validation.integration-spec.ts | 39 -- ...reate-input-validation.integration-spec.ts | 40 -- ...st-api-core-create-one.integration-spec.ts | 2 +- ...est-api-core-find-many.integration-spec.ts | 5 +- 114 files changed, 1189 insertions(+), 3524 deletions(-) create mode 100644 packages/twenty-server/src/engine/api/common/common-args-handlers/common-query-selected-fields/common-arg-handlers.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/common-args-processors.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/data-arg.processor.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/__tests__/transform-actor-field.util.spec.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/__tests__/transform-address-field.util.spec.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/__tests__/transform-array-field.util.spec.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/__tests__/transform-currency-field.util.spec.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/__tests__/transform-full-name-field.util.spec.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/__tests__/transform-numeric-field.util.spec.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/__tests__/transform-raw-json-field.util.spec.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/__tests__/transform-text-field.util.spec.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-actor-field.util.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-address-field.util.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-array-field.util.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-currency-field.util.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-full-name-field.util.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-numeric-field.util.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-raw-json-field.util.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-text-field.util.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-actor-field-or-throw.util.spec.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-address-field-or-throw.util.spec.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-array-field-or-throw.util.spec.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-boolean-field-or-throw.util.spec.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-currency-field-or-throw.util.spec.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-date-and-date-time-field-or-throw.util.spec.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-emails-field-or-throw.util.spec.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-full-name-field-or-throw.util.spec.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-links-field-or-throw.util.spec.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-multi-select-field-or-throw.util.spec.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-number-field-or-throw.util.spec.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-numeric-field-or-throw.util.spec.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-phones-field-or-throw.util.spec.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-rating-and-select-field-or-throw.util.spec.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-raw-json-field-or-throw.util.spec.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-rich-text-v2-field-or-throw.util.spec.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-text-field-or-throw.util.spec.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-uuid-field-or-throw.util.spec.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-actor-field-or-throw.util.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-address-field-or-throw.util.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-array-field-or-throw.util.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-boolean-field-or-throw.util.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-currency-field-or-throw.util.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-date-and-date-time-field-or-throw.util.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-emails-field-or-throw.util.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-full-name-field-or-throw.util.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-links-field-or-throw.util.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-multi-select-field-or-throw.util.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-number-field-or-throw.util.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-numeric-field-or-throw.util.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-phones-field-or-throw.util.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-rating-and-select-field-or-throw.util.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-raw-json-field-or-throw.util.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-rich-text-v2-field-or-throw.util.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-text-field-or-throw.util.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-uuid-field-or-throw.util.ts delete mode 100644 packages/twenty-server/src/engine/api/common/common-args-processors/query-runner-args.factory.ts create mode 100644 packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/__tests__/query-runner-args.factory.spec.ts create mode 100644 packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/index.ts create mode 100644 packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/query-result-getters.factory.ts create mode 100644 packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-runner-args.factory.ts delete mode 100644 packages/twenty-server/src/engine/core-modules/record-transformer/utils/transform-emails-value.util.ts delete mode 100644 packages/twenty-server/src/engine/core-modules/record-transformer/utils/transform-rich-text-v2.util.ts delete mode 100644 packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/address-field-create-input-validation.integration-spec.ts.snap delete mode 100644 packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/currency-field-create-input-validation.integration-spec.ts.snap delete mode 100644 packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/emails-field-create-input-validation.integration-spec.ts.snap delete mode 100644 packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/full-name-field-create-input-validation.integration-spec.ts.snap delete mode 100644 packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/links-field-create-input-validation.integration-spec.ts.snap delete mode 100644 packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/phones-field-create-input-validation.integration-spec.ts.snap delete mode 100644 packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/rich-text-v2-field-create-input-validation.integration-spec.ts.snap diff --git a/packages/twenty-server/src/engine/api/common/common-args-handlers/common-query-selected-fields/common-arg-handlers.ts b/packages/twenty-server/src/engine/api/common/common-args-handlers/common-query-selected-fields/common-arg-handlers.ts new file mode 100644 index 0000000000..7b3b26e558 --- /dev/null +++ b/packages/twenty-server/src/engine/api/common/common-args-handlers/common-query-selected-fields/common-arg-handlers.ts @@ -0,0 +1,3 @@ +import { CommonSelectedFieldsHandler } from 'src/engine/api/common/common-args-handlers/common-query-selected-fields/common-selected-fields.handler'; + +export const CommonArgsHandlers = [CommonSelectedFieldsHandler]; diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/common-args-processors.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/common-args-processors.ts deleted file mode 100644 index 355d598869..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/common-args-processors.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { DataArgProcessor } from 'src/engine/api/common/common-args-processors/data-arg-processor/data-arg.processor'; -import { QueryRunnerArgsFactory } from 'src/engine/api/common/common-args-processors/query-runner-args.factory'; - -export const CommonArgsProcessors = [DataArgProcessor, QueryRunnerArgsFactory]; // TODO: Refacto-common Remove QueryRunnerArgsFactory diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/data-arg.processor.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/data-arg.processor.ts deleted file mode 100644 index 0f41d7d166..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/data-arg.processor.ts +++ /dev/null @@ -1,271 +0,0 @@ -import { Injectable } from '@nestjs/common'; - -import { isNull, isUndefined } from '@sniptt/guards'; -import { - FieldMetadataRelationSettings, - FieldMetadataType, - ObjectRecord, - RelationType, -} from 'twenty-shared/types'; -import { - assertIsDefinedOrThrow, - assertUnreachable, - isDefined, -} from 'twenty-shared/utils'; - -import { transformActorField } from 'src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-actor-field.util'; -import { transformAddressField } from 'src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-address-field.util'; -import { transformArrayField } from 'src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-array-field.util'; -import { transformCurrencyField } from 'src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-currency-field.util'; -import { transformFullNameField } from 'src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-full-name-field.util'; -import { transformNumericField } from 'src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-numeric-field.util'; -import { transformRawJsonField } from 'src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-raw-json-field.util'; -import { transformTextField } from 'src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-text-field.util'; -import { validateActorFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-actor-field-or-throw.util'; -import { validateAddressFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-address-field-or-throw.util'; -import { validateArrayFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-array-field-or-throw.util'; -import { validateBooleanFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-boolean-field-or-throw.util'; -import { validateCurrencyFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-currency-field-or-throw.util'; -import { validateDateAndDateTimeFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-date-and-date-time-field-or-throw.util'; -import { validateEmailsFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-emails-field-or-throw.util'; -import { validateFullNameFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-full-name-field-or-throw.util'; -import { validateLinksFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-links-field-or-throw.util'; -import { validateMultiSelectFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-multi-select-field-or-throw.util'; -import { validateNumberFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-number-field-or-throw.util'; -import { validateNumericFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-numeric-field-or-throw.util'; -import { validatePhonesFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-phones-field-or-throw.util'; -import { validateRatingAndSelectFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-rating-and-select-field-or-throw.util'; -import { validateRawJsonFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-raw-json-field-or-throw.util'; -import { validateRichTextV2FieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-rich-text-v2-field-or-throw.util'; -import { validateTextFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-text-field-or-throw.util'; -import { validateUUIDFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-uuid-field-or-throw.util'; -import { - CommonQueryRunnerException, - CommonQueryRunnerExceptionCode, -} from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception'; -import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type'; -import { RecordPositionService } from 'src/engine/core-modules/record-position/services/record-position.service'; -import { transformEmailsValue } from 'src/engine/core-modules/record-transformer/utils/transform-emails-value.util'; -import { transformLinksValue } from 'src/engine/core-modules/record-transformer/utils/transform-links-value.util'; -import { transformPhonesValue } from 'src/engine/core-modules/record-transformer/utils/transform-phones-value.util'; -import { transformRichTextV2Value } from 'src/engine/core-modules/record-transformer/utils/transform-rich-text-v2.util'; -import { WorkspaceNotFoundDefaultError } from 'src/engine/core-modules/workspace/workspace.exception'; -import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; -import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps'; - -@Injectable() -export class DataArgProcessor { - constructor(private readonly recordPositionService: RecordPositionService) {} - - async process({ - partialRecordInputs, - authContext, - objectMetadataItemWithFieldMaps, - shouldBackfillPositionIfUndefined = true, - }: { - partialRecordInputs: Partial[] | undefined; - authContext: AuthContext; - objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps; - shouldBackfillPositionIfUndefined?: boolean; - }): Promise[]> { - if (!isDefined(partialRecordInputs)) { - return []; - } - - const workspace = authContext.workspace; - - assertIsDefinedOrThrow(workspace, WorkspaceNotFoundDefaultError); - - const processedRecords: Partial[] = []; - - for (const record of partialRecordInputs) { - const processedRecord: Partial = {}; - - for (const [key, value] of Object.entries(record)) { - const fieldMetadataId = - objectMetadataItemWithFieldMaps.fieldIdByName[key] || - objectMetadataItemWithFieldMaps.fieldIdByJoinColumnName[key]; - - if (!isDefined(fieldMetadataId)) { - throw new CommonQueryRunnerException( - `Object ${objectMetadataItemWithFieldMaps.nameSingular} doesn't have any "${key}" field.`, - CommonQueryRunnerExceptionCode.INVALID_ARGS_DATA, - ); - } - - const fieldMetadata = - objectMetadataItemWithFieldMaps.fieldsById[fieldMetadataId]; - - if ( - !isDefined(fieldMetadata.defaultValue) && - !fieldMetadata.isNullable && - isNull(value) - ) { - throw new CommonQueryRunnerException( - `Field ${key} is not nullable and has no default value.`, - CommonQueryRunnerExceptionCode.INVALID_ARGS_DATA, - ); - } - - if (isUndefined(value)) { - continue; - } - - processedRecord[key] = await this.processField( - fieldMetadata, - key, - value, - ); - } - processedRecords.push(processedRecord); - } - - const overriddenPositionRecords = - await this.recordPositionService.overridePositionOnRecords({ - partialRecordInputs: processedRecords, - workspaceId: workspace.id, - objectMetadata: { - isCustom: objectMetadataItemWithFieldMaps.isCustom, - nameSingular: objectMetadataItemWithFieldMaps.nameSingular, - fieldIdByName: objectMetadataItemWithFieldMaps.fieldIdByName, - }, - shouldBackfillPositionIfUndefined, - }); - - return overriddenPositionRecords; - } - - private async processField( - fieldMetadata: FieldMetadataEntity, - key: string, - value: unknown, - ): Promise { - switch (fieldMetadata.type) { - case FieldMetadataType.POSITION: - return value; - case FieldMetadataType.NUMERIC: { - const validatedValue = validateNumericFieldOrThrow(value, key); - - return transformNumericField(validatedValue); - } - case FieldMetadataType.NUMBER: { - return validateNumberFieldOrThrow(value, key); - } - case FieldMetadataType.TEXT: { - const validatedValue = validateTextFieldOrThrow(value, key); - - return transformTextField(validatedValue); - } - case FieldMetadataType.DATE_TIME: - case FieldMetadataType.DATE: - return validateDateAndDateTimeFieldOrThrow(value, key); - case FieldMetadataType.BOOLEAN: - return validateBooleanFieldOrThrow(value, key); - case FieldMetadataType.RATING: - case FieldMetadataType.SELECT: { - validateRatingAndSelectFieldOrThrow( - value, - key, - fieldMetadata.options?.map((option) => option.value), - ); - - return value; - } - - case FieldMetadataType.MULTI_SELECT: { - const validatedValue = validateMultiSelectFieldOrThrow( - value, - key, - fieldMetadata.options?.map((option) => option.value), - ); - - return transformArrayField(validatedValue); - } - case FieldMetadataType.UUID: - return validateUUIDFieldOrThrow(value, key); - case FieldMetadataType.ARRAY: { - const validatedValue = validateArrayFieldOrThrow(value, key); - - return transformArrayField(validatedValue); - } - case FieldMetadataType.RAW_JSON: { - const validatedValue = validateRawJsonFieldOrThrow(value, key); - - return transformRawJsonField(validatedValue); - } - case FieldMetadataType.RELATION: - case FieldMetadataType.MORPH_RELATION: { - const fieldMetadataRelationSettings = - fieldMetadata.settings as FieldMetadataRelationSettings; - - if ( - fieldMetadataRelationSettings.relationType === - RelationType.ONE_TO_MANY - ) { - throw new CommonQueryRunnerException( - `One-to-many relation ${key} field does not support write operations.`, - CommonQueryRunnerExceptionCode.INVALID_ARGS_DATA, - ); - } - - if (key === fieldMetadataRelationSettings.joinColumnName) { - return validateUUIDFieldOrThrow(value, key); - } - - return value; - } - case FieldMetadataType.PHONES: { - const validatedValue = validatePhonesFieldOrThrow(value, key); - - return transformPhonesValue({ input: validatedValue }); - } - case FieldMetadataType.EMAILS: { - const validatedValue = validateEmailsFieldOrThrow(value, key); - - return transformEmailsValue(validatedValue); - } - case FieldMetadataType.FULL_NAME: { - const validatedValue = validateFullNameFieldOrThrow(value, key); - - return transformFullNameField(validatedValue); - } - - case FieldMetadataType.ADDRESS: { - const validatedValue = validateAddressFieldOrThrow(value, key); - - return transformAddressField(validatedValue); - } - case FieldMetadataType.CURRENCY: { - const validatedValue = validateCurrencyFieldOrThrow(value, key); - - return transformCurrencyField(validatedValue); - } - case FieldMetadataType.ACTOR: { - const validatedValue = validateActorFieldOrThrow(value, key); - - return transformActorField(validatedValue); - } - case FieldMetadataType.RICH_TEXT_V2: { - const validatedValue = validateRichTextV2FieldOrThrow(value, key); - - return await transformRichTextV2Value(validatedValue); - } - case FieldMetadataType.LINKS: { - const validatedValue = validateLinksFieldOrThrow(value, key); - - return transformLinksValue(validatedValue); - } - case FieldMetadataType.RICH_TEXT: - case FieldMetadataType.TS_VECTOR: - throw new CommonQueryRunnerException( - `${key} ${fieldMetadata.type}-typed field does not support write operations`, - CommonQueryRunnerExceptionCode.INVALID_ARGS_DATA, - ); - default: - assertUnreachable( - fieldMetadata.type, - 'Should never occur, add validator for new field type', - ); - } - } -} diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/__tests__/transform-actor-field.util.spec.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/__tests__/transform-actor-field.util.spec.ts deleted file mode 100644 index 20bbfc6b05..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/__tests__/transform-actor-field.util.spec.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { FieldActorSource } from 'twenty-shared/types'; - -import { transformActorField } from 'src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-actor-field.util'; - -describe('transformActorField', () => { - it('should return null when value is null', () => { - const result = transformActorField(null, true); - - expect(result).toBeNull(); - }); - - it('should transform actor with source only', () => { - const result = transformActorField( - { - source: FieldActorSource.EMAIL, - }, - true, - ); - - expect(result).toEqual({ - source: FieldActorSource.EMAIL, - }); - }); - - it('should transform actor with source and context', () => { - const result = transformActorField( - { - source: FieldActorSource.WORKFLOW, - context: { workflowId: '123', stepId: 'step-1' }, - }, - true, - ); - - expect(result).toEqual({ - source: FieldActorSource.WORKFLOW, - context: { workflowId: '123', stepId: 'step-1' }, - }); - }); - - it('should transform actor with null source', () => { - const result = transformActorField( - { - source: null, - context: { userId: '456' }, - }, - true, - ); - - expect(result).toEqual({ - source: null, - context: { userId: '456' }, - }); - }); - - it('should transform actor with null context', () => { - const result = transformActorField( - { - source: FieldActorSource.API, - context: null, - }, - true, - ); - - expect(result).toEqual({ - source: FieldActorSource.API, - context: null, - }); - }); - - it('should transform empty context object to null', () => { - const result = transformActorField( - { - source: FieldActorSource.EMAIL, - context: {}, - }, - true, - ); - - expect(result).toEqual({ - source: FieldActorSource.EMAIL, - context: null, - }); - }); -}); diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/__tests__/transform-address-field.util.spec.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/__tests__/transform-address-field.util.spec.ts deleted file mode 100644 index 6366a5f386..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/__tests__/transform-address-field.util.spec.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { transformAddressField } from 'src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-address-field.util'; - -describe('transformAddressField', () => { - it('should return null when value is null', () => { - const result = transformAddressField(null); - - expect(result).toBeNull(); - }); - - it('should return an empty object when value is an empty object', () => { - const result = transformAddressField({}); - - expect(result).toEqual({}); - }); - - it('should preserve undefined for fields that are not provided', () => { - const result = transformAddressField({ - addressStreet1: '123 Main St', - addressCity: 'San Francisco', - }); - - expect(result).toEqual({ - addressStreet1: '123 Main St', - addressCity: 'San Francisco', - }); - }); - - it('should handle mixed null, undefined, and valid values', () => { - const result = transformAddressField({ - addressStreet1: '123 Main St', - addressStreet2: null, - addressCity: 'San Francisco', - addressLat: 37.7749, - addressLng: null, - }); - - expect(result).toEqual({ - addressStreet1: '123 Main St', - addressStreet2: null, - addressCity: 'San Francisco', - addressLat: 37.7749, - addressLng: null, - }); - }); -}); diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/__tests__/transform-array-field.util.spec.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/__tests__/transform-array-field.util.spec.ts deleted file mode 100644 index b140022234..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/__tests__/transform-array-field.util.spec.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { transformArrayField } from 'src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-array-field.util'; - -describe('transformArrayField', () => { - it('should return null when value is null', () => { - const result = transformArrayField(null, true); - - expect(result).toBeNull(); - }); - - it('should return null when value is an empty array', () => { - const result = transformArrayField([], true); - - expect(result).toBeNull(); - }); - - it('should return an array when value is a string', () => { - const result = transformArrayField('singleString', true); - - expect(result).toEqual(['singleString']); - }); - - it('should return an array when value is an array of strings', () => { - const result = transformArrayField(['string1', 'string2', 'string3'], true); - - expect(result).toEqual(['string1', 'string2', 'string3']); - }); -}); diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/__tests__/transform-currency-field.util.spec.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/__tests__/transform-currency-field.util.spec.ts deleted file mode 100644 index b577065803..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/__tests__/transform-currency-field.util.spec.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { transformCurrencyField } from 'src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-currency-field.util'; - -describe('transformCurrencyField', () => { - it('should return null when value is null', () => { - const result = transformCurrencyField(null); - - expect(result).toBeNull(); - }); - - it('should return empty object when value is empty object', () => { - const result = transformCurrencyField({}); - - expect(result).toEqual({}); - }); - - it('should transform amountMicros from number', () => { - const result = transformCurrencyField({ amountMicros: 1000 }); - - expect(result).toEqual({ amountMicros: 1000 }); - }); - - it('should transform amountMicros from string to number', () => { - const result = transformCurrencyField({ amountMicros: '1000' }); - - expect(result).toEqual({ amountMicros: 1000 }); - }); - - it('should transform currencyCode', () => { - const result = transformCurrencyField({ currencyCode: 'USD' }); - - expect(result).toEqual({ currencyCode: 'USD' }); - }); - - it('should transform both amountMicros and currencyCode', () => { - const result = transformCurrencyField({ - amountMicros: '2500', - currencyCode: 'EUR', - }); - - expect(result).toEqual({ amountMicros: 2500, currencyCode: 'EUR' }); - }); - - it('should handle null amountMicros', () => { - const result = transformCurrencyField({ amountMicros: null }); - - expect(result).toEqual({ amountMicros: null }); - }); - - it('should handle null currencyCode', () => { - const result = transformCurrencyField({ currencyCode: null }); - - expect(result).toEqual({ currencyCode: null }); - }); -}); diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/__tests__/transform-full-name-field.util.spec.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/__tests__/transform-full-name-field.util.spec.ts deleted file mode 100644 index 2f2716509b..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/__tests__/transform-full-name-field.util.spec.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { transformFullNameField } from 'src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-full-name-field.util'; - -describe('transformFullNameField', () => { - it('should return null when value is null', () => { - const result = transformFullNameField(null, true); - - expect(result).toBeNull(); - }); - - it('should return full name object with both fields', () => { - const value = { - firstName: 'John', - lastName: 'Doe', - }; - const result = transformFullNameField(value, true); - - expect(result).toEqual({ - firstName: 'John', - lastName: 'Doe', - }); - }); - - it('should return full name object with only lastName', () => { - const value = { - lastName: '', - }; - const result = transformFullNameField(value, true); - - expect(result).toEqual({ - lastName: null, - }); - }); -}); diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/__tests__/transform-numeric-field.util.spec.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/__tests__/transform-numeric-field.util.spec.ts deleted file mode 100644 index 8541b96890..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/__tests__/transform-numeric-field.util.spec.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { transformNumericField } from 'src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-numeric-field.util'; - -describe('transformNumericField', () => { - it('should return null when value is null', () => { - const result = transformNumericField(null); - - expect(result).toBeNull(); - }); - - it('should return the number when value is a float', () => { - const result = transformNumericField(3.14159); - - expect(result).toBe(3.14159); - }); - - it('should transform a numeric string with decimals to a number', () => { - const result = transformNumericField('123.456'); - - expect(result).toBe(123.456); - }); -}); diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/__tests__/transform-raw-json-field.util.spec.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/__tests__/transform-raw-json-field.util.spec.ts deleted file mode 100644 index 600c2a63aa..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/__tests__/transform-raw-json-field.util.spec.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { transformRawJsonField } from 'src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-raw-json-field.util'; - -describe('transformRawJsonField', () => { - it('should return null when value is null', () => { - const result = transformRawJsonField(null, true); - - expect(result).toBeNull(); - }); - - it('should return null when value is empty object', () => { - const result = transformRawJsonField({}, true); - - expect(result).toBeNull(); - }); - - it('should return the string when value is empty array', () => { - const result = transformRawJsonField([], true); - - expect(result).toBeNull(); - }); -}); diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/__tests__/transform-text-field.util.spec.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/__tests__/transform-text-field.util.spec.ts deleted file mode 100644 index cfdffb2783..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/__tests__/transform-text-field.util.spec.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { transformTextField } from 'src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-text-field.util'; - -describe('transformTextField', () => { - it('should return null when value is null', () => { - const result = transformTextField(null, true); - - expect(result).toBeNull(); - }); - - it('should return null when value is empty string', () => { - const result = transformTextField('', true); - - expect(result).toBeNull(); - }); - - it('should return the string when value is a non-empty string', () => { - const result = transformTextField('hello world', true); - - expect(result).toBe('hello world'); - }); -}); diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-actor-field.util.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-actor-field.util.ts deleted file mode 100644 index 11e9e3b4af..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-actor-field.util.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { isNull, isUndefined } from '@sniptt/guards'; -import { type FieldActorSource } from 'twenty-shared/types'; - -import { transformRawJsonField } from 'src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-raw-json-field.util'; - -export const transformActorField = ( - value: { - source?: FieldActorSource | null; - context?: object | null; - } | null, - isNullEquivalenceEnabled: boolean = false, -): { - source?: FieldActorSource | null; - context?: object | null; -} | null => { - if (isNull(value)) return null; - - return { - source: value.source, - context: isUndefined(value.context) - ? undefined - : transformRawJsonField(value.context, isNullEquivalenceEnabled), - }; -}; diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-address-field.util.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-address-field.util.ts deleted file mode 100644 index 537f6d148b..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-address-field.util.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { isNull, isUndefined } from '@sniptt/guards'; - -import { transformNumericField } from 'src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-numeric-field.util'; -import { transformTextField } from 'src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-text-field.util'; - -export const transformAddressField = ( - value: { - addressStreet1?: string | null; - addressStreet2?: string | null; - addressCity?: string | null; - addressState?: string | null; - addressPostcode?: string | null; - addressCountry?: string | null; - addressLat?: number | null; - addressLng?: number | null; - } | null, - isNullEquivalenceEnabled: boolean = false, -): { - addressStreet1?: string | null; - addressStreet2?: string | null; - addressCity?: string | null; - addressState?: string | null; - addressPostcode?: string | null; - addressCountry?: string | null; - addressLat?: number | null; - addressLng?: number | null; -} | null => { - if (isNull(value)) return null; - - return { - addressStreet1: isUndefined(value.addressStreet1) - ? undefined - : transformTextField(value.addressStreet1, isNullEquivalenceEnabled), - addressStreet2: isUndefined(value.addressStreet2) - ? undefined - : transformTextField(value.addressStreet2, isNullEquivalenceEnabled), - addressCity: isUndefined(value.addressCity) - ? undefined - : transformTextField(value.addressCity, isNullEquivalenceEnabled), - addressState: isUndefined(value.addressState) - ? undefined - : transformTextField(value.addressState, isNullEquivalenceEnabled), - addressPostcode: isUndefined(value.addressPostcode) - ? undefined - : transformTextField(value.addressPostcode, isNullEquivalenceEnabled), - addressCountry: isUndefined(value.addressCountry) - ? undefined - : transformTextField(value.addressCountry, isNullEquivalenceEnabled), - addressLat: isUndefined(value.addressLat) - ? undefined - : transformNumericField(value.addressLat), - addressLng: isUndefined(value.addressLng) - ? undefined - : transformNumericField(value.addressLng), - }; -}; diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-array-field.util.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-array-field.util.ts deleted file mode 100644 index 47c0d34672..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-array-field.util.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { isNull } from '@sniptt/guards'; - -export const transformArrayField = ( - value: string | string[] | null, - isNullEquivalenceEnabled: boolean = false, -): string[] | null => { - if (typeof value === 'string') return [value]; - - return isNullEquivalenceEnabled && - !isNull(value) && - Object.keys(value).length === 0 - ? null - : value; -}; diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-currency-field.util.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-currency-field.util.ts deleted file mode 100644 index 7481f4b49d..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-currency-field.util.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { isNull, isUndefined } from '@sniptt/guards'; - -import { transformNumericField } from 'src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-numeric-field.util'; -import { transformTextField } from 'src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-text-field.util'; - -export const transformCurrencyField = ( - value: { - amountMicros?: number | string | null; - currencyCode?: string | null; - } | null, - isNullEquivalenceEnabled: boolean = false, -): { - amountMicros?: number | null; - currencyCode?: string | null; -} | null => { - if (isNull(value)) return null; - - return { - amountMicros: isUndefined(value.amountMicros) - ? undefined - : transformNumericField(value.amountMicros), - currencyCode: isUndefined(value.currencyCode) - ? undefined - : transformTextField(value.currencyCode, isNullEquivalenceEnabled), - }; -}; diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-full-name-field.util.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-full-name-field.util.ts deleted file mode 100644 index 90dec4d07e..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-full-name-field.util.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { isNull, isUndefined } from '@sniptt/guards'; - -import { transformTextField } from 'src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-text-field.util'; - -export const transformFullNameField = ( - value: { - firstName?: string | null; - lastName?: string | null; - } | null, - isNullEquivalenceEnabled: boolean = false, -): { - firstName?: string | null; - lastName?: string | null; -} | null => { - if (isNull(value)) return null; - - return { - firstName: isUndefined(value.firstName) - ? undefined - : transformTextField(value.firstName, isNullEquivalenceEnabled), - lastName: isUndefined(value.lastName) - ? undefined - : transformTextField(value.lastName, isNullEquivalenceEnabled), - }; -}; diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-numeric-field.util.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-numeric-field.util.ts deleted file mode 100644 index 598990df68..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-numeric-field.util.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { isNull } from '@sniptt/guards'; - -export const transformNumericField = ( - value: number | string | null, -): number | null => { - return isNull(value) ? null : Number(value); -}; diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-raw-json-field.util.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-raw-json-field.util.ts deleted file mode 100644 index 804e038b72..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-raw-json-field.util.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { isNull } from '@sniptt/guards'; - -export const transformRawJsonField = ( - value: object | null, - isNullEquivalenceEnabled: boolean = false, -): object | null => { - return isNullEquivalenceEnabled && - !isNull(value) && - Object.keys(value).length === 0 - ? null - : value; -}; diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-text-field.util.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-text-field.util.ts deleted file mode 100644 index 059bd34e0a..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-text-field.util.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { isNonEmptyString } from '@sniptt/guards'; - -export const transformTextField = ( - value: string | null, - isNullEquivalenceEnabled: boolean = false, -): string | null => { - return isNullEquivalenceEnabled && !isNonEmptyString(value) ? null : value; -}; diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-actor-field-or-throw.util.spec.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-actor-field-or-throw.util.spec.ts deleted file mode 100644 index 0c2f86754d..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-actor-field-or-throw.util.spec.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { FieldActorSource } from 'twenty-shared/types'; - -import { validateActorFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-actor-field-or-throw.util'; -import { CommonQueryRunnerException } from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception'; - -describe('validateActorFieldOrThrow', () => { - describe('valid inputs', () => { - it('should return null when value is null', () => { - const result = validateActorFieldOrThrow(null, 'testField'); - - expect(result).toBeNull(); - }); - - it('should return valid actor object with source and context', () => { - const validActor = { - source: FieldActorSource.EMAIL, - context: { userId: '123', email: 'test@example.com' }, - }; - - const result = validateActorFieldOrThrow(validActor, 'testField'); - - expect(result).toEqual(validActor); - }); - - it('should accept empty context object', () => { - const validActor = { - source: FieldActorSource.EMAIL, - context: {}, - }; - - const result = validateActorFieldOrThrow(validActor, 'testField'); - - expect(result).toEqual(validActor); - }); - }); - - describe('invalid inputs', () => { - it('should throw when value is undefined', () => { - expect(() => validateActorFieldOrThrow(undefined, 'testField')).toThrow( - CommonQueryRunnerException, - ); - }); - - it('should throw when value is a string', () => { - expect(() => validateActorFieldOrThrow('invalid', 'testField')).toThrow( - CommonQueryRunnerException, - ); - }); - - it('should throw when source is invalid', () => { - const invalidActor = { - source: 'INVALID_SOURCE', - context: {}, - }; - - expect(() => - validateActorFieldOrThrow(invalidActor, 'testField'), - ).toThrow(CommonQueryRunnerException); - }); - - it('should throw when context is a string', () => { - const invalidActor = { - source: FieldActorSource.EMAIL, - context: 'invalid', - }; - - expect(() => - validateActorFieldOrThrow(invalidActor, 'testField'), - ).toThrow(CommonQueryRunnerException); - }); - - it('should throw when actor has invalid subfield', () => { - const invalidActor = { - source: FieldActorSource.EMAIL, - context: {}, - invalidField: 'invalid', - }; - - expect(() => - validateActorFieldOrThrow(invalidActor, 'testField'), - ).toThrow(CommonQueryRunnerException); - }); - }); -}); diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-address-field-or-throw.util.spec.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-address-field-or-throw.util.spec.ts deleted file mode 100644 index da2c4b6c5d..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-address-field-or-throw.util.spec.ts +++ /dev/null @@ -1,159 +0,0 @@ -import { validateAddressFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-address-field-or-throw.util'; -import { CommonQueryRunnerException } from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception'; - -describe('validateAddressFieldOrThrow', () => { - describe('valid inputs', () => { - it('should return null when value is null', () => { - const result = validateAddressFieldOrThrow(null, 'testField'); - - expect(result).toBeNull(); - }); - - it('should return valid address object with all text fields', () => { - const value = { - addressStreet1: '123 Main St', - addressStreet2: 'Apt 4B', - addressCity: 'New York', - addressState: 'NY', - addressPostcode: '10001', - addressCountry: 'USA', - }; - const result = validateAddressFieldOrThrow(value, 'testField'); - - expect(result).toEqual(value); - }); - - it('should return valid address object with coordinates', () => { - const value = { - addressStreet1: '123 Main St', - addressCity: 'New York', - addressLat: 40.7128, - addressLng: -74.006, - }; - const result = validateAddressFieldOrThrow(value, 'testField'); - - expect(result).toEqual(value); - }); - - it('should return valid address object with null subfields', () => { - const value = { - addressStreet1: '123 Main St', - addressStreet2: null, - addressCity: 'New York', - addressState: null, - addressPostcode: null, - addressCountry: null, - addressLat: null, - addressLng: null, - }; - const result = validateAddressFieldOrThrow(value, 'testField'); - - expect(result).toEqual(value); - }); - }); - - describe('invalid inputs', () => { - it('should throw when value is not an object', () => { - expect(() => validateAddressFieldOrThrow('invalid', 'testField')).toThrow( - CommonQueryRunnerException, - ); - }); - - it('should throw when value is an array', () => { - expect(() => - validateAddressFieldOrThrow(['123 Main St'], 'testField'), - ).toThrow(CommonQueryRunnerException); - }); - - it('should throw when addressStreet1 is not a string or number', () => { - const value = { - addressStreet1: { invalid: 'object' }, - }; - - expect(() => validateAddressFieldOrThrow(value, 'testField')).toThrow( - CommonQueryRunnerException, - ); - }); - - it('should throw when addressStreet2 is not a string', () => { - const value = { - addressStreet2: 123, - }; - - expect(() => validateAddressFieldOrThrow(value, 'testField')).toThrow( - CommonQueryRunnerException, - ); - }); - - it('should throw when addressCity is not a string', () => { - const value = { - addressCity: true, - }; - - expect(() => validateAddressFieldOrThrow(value, 'testField')).toThrow( - CommonQueryRunnerException, - ); - }); - - it('should throw when addressState is not a string', () => { - const value = { - addressState: ['NY'], - }; - - expect(() => validateAddressFieldOrThrow(value, 'testField')).toThrow( - CommonQueryRunnerException, - ); - }); - - it('should throw when addressPostcode is not a string', () => { - const value = { - addressPostcode: 10001, - }; - - expect(() => validateAddressFieldOrThrow(value, 'testField')).toThrow( - CommonQueryRunnerException, - ); - }); - - it('should throw when addressCountry is not a string', () => { - const value = { - addressCountry: { code: 'US' }, - }; - - expect(() => validateAddressFieldOrThrow(value, 'testField')).toThrow( - CommonQueryRunnerException, - ); - }); - - it('should throw when addressLat is not a number', () => { - const value = { - addressLat: 'not a number', - }; - - expect(() => validateAddressFieldOrThrow(value, 'testField')).toThrow( - CommonQueryRunnerException, - ); - }); - - it('should throw when addressLng is not a number', () => { - const value = { - addressLng: 'not a number', - }; - - expect(() => validateAddressFieldOrThrow(value, 'testField')).toThrow( - CommonQueryRunnerException, - ); - }); - - it('should throw when an invalid subfield is provided', () => { - const value = { - addressStreet1: '123 Main St', - invalidSubField: 'invalid', - }; - - expect(() => validateAddressFieldOrThrow(value, 'testField')).toThrow( - CommonQueryRunnerException, - ); - }); - }); -}); diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-array-field-or-throw.util.spec.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-array-field-or-throw.util.spec.ts deleted file mode 100644 index 7766d61210..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-array-field-or-throw.util.spec.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { validateArrayFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-array-field-or-throw.util'; -import { CommonQueryRunnerException } from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception'; - -describe('validateArrayFieldOrThrow', () => { - describe('valid inputs', () => { - it('should return null when value is null', () => { - const result = validateArrayFieldOrThrow(null, 'testField'); - - expect(result).toBeNull(); - }); - - it('should return the string when value is a string', () => { - const result = validateArrayFieldOrThrow('singleString', 'testField'); - - expect(result).toBe('singleString'); - }); - - it('should return the array when value is an empty array', () => { - const result = validateArrayFieldOrThrow([], 'testField'); - - expect(result).toEqual([]); - }); - - it('should return the array when value is an array of strings', () => { - const stringArray = ['string1', 'string2', 'string3']; - const result = validateArrayFieldOrThrow(stringArray, 'testField'); - - expect(result).toEqual(stringArray); - }); - }); - - describe('invalid inputs', () => { - it('should throw when value is a number', () => { - expect(() => validateArrayFieldOrThrow(123, 'testField')).toThrow( - CommonQueryRunnerException, - ); - }); - - it('should throw when value is an object', () => { - const objectValue = { key: 'value' }; - - expect(() => validateArrayFieldOrThrow(objectValue, 'testField')).toThrow( - CommonQueryRunnerException, - ); - }); - - it('should throw when value is an array containing objects', () => { - const arrayWithObjects = ['string1', { key: 'value' }, 'string2']; - - expect(() => - validateArrayFieldOrThrow(arrayWithObjects, 'testField'), - ).toThrow(CommonQueryRunnerException); - }); - }); -}); diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-boolean-field-or-throw.util.spec.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-boolean-field-or-throw.util.spec.ts deleted file mode 100644 index 078aad3b84..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-boolean-field-or-throw.util.spec.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { validateBooleanFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-boolean-field-or-throw.util'; -import { CommonQueryRunnerException } from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception'; - -describe('validateBooleanFieldOrThrow', () => { - describe('valid inputs', () => { - it('should return null when value is null', () => { - const result = validateBooleanFieldOrThrow(null, 'testField'); - - expect(result).toBeNull(); - }); - - it('should return true when value is true', () => { - const result = validateBooleanFieldOrThrow(true, 'testField'); - - expect(result).toBe(true); - }); - - it('should return false when value is false', () => { - const result = validateBooleanFieldOrThrow(false, 'testField'); - - expect(result).toBe(false); - }); - }); - - describe('invalid inputs', () => { - it('should throw when value is undefined', () => { - expect(() => validateBooleanFieldOrThrow(undefined, 'testField')).toThrow( - CommonQueryRunnerException, - ); - }); - - it('should throw when value is a string "true"', () => { - expect(() => validateBooleanFieldOrThrow('true', 'testField')).toThrow( - CommonQueryRunnerException, - ); - }); - - it('should throw when value is an empty string', () => { - expect(() => validateBooleanFieldOrThrow('', 'testField')).toThrow( - CommonQueryRunnerException, - ); - }); - - it('should throw when value is a number 1', () => { - expect(() => validateBooleanFieldOrThrow(1, 'testField')).toThrow( - CommonQueryRunnerException, - ); - }); - - it('should throw when value is a number 0', () => { - expect(() => validateBooleanFieldOrThrow(0, 'testField')).toThrow( - CommonQueryRunnerException, - ); - }); - - it('should throw when value is an object', () => { - expect(() => validateBooleanFieldOrThrow({}, 'testField')).toThrow( - CommonQueryRunnerException, - ); - }); - - it('should throw when value is a function', () => { - const functionValue = () => true; - - expect(() => - validateBooleanFieldOrThrow(functionValue, 'testField'), - ).toThrow(CommonQueryRunnerException); - }); - }); -}); diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-currency-field-or-throw.util.spec.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-currency-field-or-throw.util.spec.ts deleted file mode 100644 index 32cea3a084..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-currency-field-or-throw.util.spec.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { validateCurrencyFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-currency-field-or-throw.util'; -import { CommonQueryRunnerException } from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception'; - -describe('validateCurrencyFieldOrThrow', () => { - describe('valid inputs', () => { - it('should return null when value is null', () => { - const result = validateCurrencyFieldOrThrow(null, 'testField'); - - expect(result).toBeNull(); - }); - - it('should return the currency object when both amountMicros and currencyCode are valid', () => { - const currencyValue = { - amountMicros: 1000000, - currencyCode: 'USD', - }; - const result = validateCurrencyFieldOrThrow(currencyValue, 'testField'); - - expect(result).toEqual(currencyValue); - }); - - it('should return the currency object when only amountMicros is provided', () => { - const currencyValue = { - amountMicros: 5000000, - }; - const result = validateCurrencyFieldOrThrow(currencyValue, 'testField'); - - expect(result).toEqual(currencyValue); - }); - }); - - describe('invalid inputs', () => { - it('should throw when value is not an object', () => { - expect(() => - validateCurrencyFieldOrThrow('not an object', 'testField'), - ).toThrow(CommonQueryRunnerException); - }); - - it('should throw when amountMicros is not a valid numeric value', () => { - const currencyValue = { - amountMicros: 'not a number', - currencyCode: 'USD', - }; - - expect(() => - validateCurrencyFieldOrThrow(currencyValue, 'testField'), - ).toThrow(CommonQueryRunnerException); - }); - - it('should throw when currencyCode is not a string', () => { - const currencyValue = { - amountMicros: 1000000, - currencyCode: 123, - }; - - expect(() => - validateCurrencyFieldOrThrow(currencyValue, 'testField'), - ).toThrow(CommonQueryRunnerException); - }); - - it('should throw when an invalid subfield is present', () => { - const currencyValue = { - amountMicros: 1000000, - currencyCode: 'USD', - invalidField: 'invalid', - }; - - expect(() => - validateCurrencyFieldOrThrow(currencyValue, 'testField'), - ).toThrow(CommonQueryRunnerException); - }); - }); -}); diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-date-and-date-time-field-or-throw.util.spec.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-date-and-date-time-field-or-throw.util.spec.ts deleted file mode 100644 index da4a9f0543..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-date-and-date-time-field-or-throw.util.spec.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { validateDateAndDateTimeFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-date-and-date-time-field-or-throw.util'; -import { CommonQueryRunnerException } from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception'; - -describe('validateDateAndDateTimeFieldOrThrow', () => { - describe('valid inputs', () => { - it('should return null when value is null', () => { - const result = validateDateAndDateTimeFieldOrThrow(null, 'testField'); - - expect(result).toBeNull(); - }); - - it('should return the value when it is a valid ISO date string', () => { - const dateString = '2024-01-15'; - const result = validateDateAndDateTimeFieldOrThrow( - dateString, - 'testField', - ); - - expect(result).toBe(dateString); - }); - - it('should return the value when it is a valid ISO datetime string', () => { - const datetimeString = '2024-01-15T10:30:00Z'; - const result = validateDateAndDateTimeFieldOrThrow( - datetimeString, - 'testField', - ); - - expect(result).toBe(datetimeString); - }); - - it('should return the value when it is a Date object', () => { - const dateObject = new Date('2024-01-15'); - const result = validateDateAndDateTimeFieldOrThrow( - dateObject, - 'testField', - ); - - expect(result).toBe(dateObject); - }); - - it('should return the value when it is a timestamp number', () => { - const timestamp = Date.now(); - const result = validateDateAndDateTimeFieldOrThrow( - timestamp, - 'testField', - ); - - expect(result).toBe(timestamp); - }); - - it('should return the value when it is a Date', () => { - const date = new Date(); - const result = validateDateAndDateTimeFieldOrThrow(date, 'testField'); - - expect(result).toBe(date); - }); - }); - - describe('invalid inputs', () => { - it('should throw when value is an invalid date string', () => { - expect(() => - validateDateAndDateTimeFieldOrThrow('invalid-date', 'testField'), - ).toThrow(CommonQueryRunnerException); - }); - - it('should throw when value is an empty string', () => { - expect(() => - validateDateAndDateTimeFieldOrThrow('', 'testField'), - ).toThrow(CommonQueryRunnerException); - }); - - it('should throw when value is a boolean', () => { - expect(() => - validateDateAndDateTimeFieldOrThrow(true, 'testField'), - ).toThrow(CommonQueryRunnerException); - }); - - it('should throw when value is an array', () => { - expect(() => - validateDateAndDateTimeFieldOrThrow([], 'testField'), - ).toThrow(CommonQueryRunnerException); - }); - - it('should throw when value is an object', () => { - expect(() => - validateDateAndDateTimeFieldOrThrow({}, 'testField'), - ).toThrow(CommonQueryRunnerException); - }); - - it('should throw when value is undefined', () => { - expect(() => - validateDateAndDateTimeFieldOrThrow(undefined, 'testField'), - ).toThrow(CommonQueryRunnerException); - }); - }); -}); diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-emails-field-or-throw.util.spec.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-emails-field-or-throw.util.spec.ts deleted file mode 100644 index 0802b8aafd..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-emails-field-or-throw.util.spec.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { validateEmailsFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-emails-field-or-throw.util'; -import { CommonQueryRunnerException } from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception'; - -describe('validateEmailsFieldOrThrow', () => { - describe('valid inputs', () => { - it('should return null when value is null', () => { - const result = validateEmailsFieldOrThrow(null, 'testField'); - - expect(result).toBeNull(); - }); - - it('should return the emails object when all fields are valid', () => { - const emailsValue = { - primaryEmail: 'primary@example.com', - additionalEmails: ['secondary1@example.com', 'secondary2@example.com'], - }; - const result = validateEmailsFieldOrThrow(emailsValue, 'testField'); - - expect(result).toEqual(emailsValue); - }); - - it('should return the emails object when only primaryEmail is provided', () => { - const emailsValue = { - primaryEmail: 'primary@example.com', - }; - const result = validateEmailsFieldOrThrow(emailsValue, 'testField'); - - expect(result).toEqual(emailsValue); - }); - - it('should accept empty additionalEmails array', () => { - const emailsValue = { - primaryEmail: 'primary@example.com', - additionalEmails: [], - }; - const result = validateEmailsFieldOrThrow(emailsValue, 'testField'); - - expect(result).toEqual(emailsValue); - }); - }); - - describe('invalid inputs', () => { - it('should throw when value is not an object', () => { - expect(() => - validateEmailsFieldOrThrow('not an object', 'testField'), - ).toThrow(CommonQueryRunnerException); - }); - - it('should throw when value is undefined', () => { - expect(() => validateEmailsFieldOrThrow(undefined, 'testField')).toThrow( - CommonQueryRunnerException, - ); - }); - - it('should throw when primaryEmail is not a string', () => { - const emailsValue = { - primaryEmail: 12345, - }; - - expect(() => - validateEmailsFieldOrThrow(emailsValue, 'testField'), - ).toThrow(CommonQueryRunnerException); - }); - - it('should throw when additionalEmails is not an array', () => { - const emailsValue = { - additionalEmails: { key: 'not an array' }, - }; - - expect(() => - validateEmailsFieldOrThrow(emailsValue, 'testField'), - ).toThrow(CommonQueryRunnerException); - }); - - it('should throw when invalid subfields are present', () => { - const emailsValue = { - primaryEmail: 'primary@example.com', - invalidField1: 'invalid', - invalidField2: 'invalid', - }; - - expect(() => - validateEmailsFieldOrThrow(emailsValue, 'testField'), - ).toThrow(CommonQueryRunnerException); - }); - }); -}); diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-full-name-field-or-throw.util.spec.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-full-name-field-or-throw.util.spec.ts deleted file mode 100644 index ae4abd877e..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-full-name-field-or-throw.util.spec.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { validateFullNameFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-full-name-field-or-throw.util'; -import { CommonQueryRunnerException } from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception'; - -describe('validateFullNameFieldOrThrow', () => { - describe('valid inputs', () => { - it('should return null when value is null', () => { - const result = validateFullNameFieldOrThrow(null, 'testField'); - - expect(result).toBeNull(); - }); - - it('should return valid full name object with both fields', () => { - const value = { - firstName: 'John', - lastName: 'Doe', - }; - const result = validateFullNameFieldOrThrow(value, 'testField'); - - expect(result).toEqual(value); - }); - - it('should return valid full name object with only firstName', () => { - const value = { - firstName: 'John', - }; - const result = validateFullNameFieldOrThrow(value, 'testField'); - - expect(result).toEqual(value); - }); - - it('should return valid full name object with only lastName', () => { - const value = { - lastName: 'Doe', - }; - const result = validateFullNameFieldOrThrow(value, 'testField'); - - expect(result).toEqual(value); - }); - }); - - describe('invalid inputs', () => { - it('should throw when value is not an object', () => { - expect(() => - validateFullNameFieldOrThrow('invalid', 'testField'), - ).toThrow(CommonQueryRunnerException); - }); - - it('should throw when firstName is not a string', () => { - const value = { - firstName: { invalid: 'object' }, - }; - - expect(() => validateFullNameFieldOrThrow(value, 'testField')).toThrow( - CommonQueryRunnerException, - ); - }); - - it('should throw when an invalid subfield is provided', () => { - const value = { - firstName: 'John', - lastName: 'Doe', - invalidSubField: 'invalid', - }; - - expect(() => validateFullNameFieldOrThrow(value, 'testField')).toThrow( - CommonQueryRunnerException, - ); - }); - }); -}); diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-links-field-or-throw.util.spec.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-links-field-or-throw.util.spec.ts deleted file mode 100644 index 6e1b6a202d..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-links-field-or-throw.util.spec.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { validateLinksFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-links-field-or-throw.util'; -import { CommonQueryRunnerException } from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception'; - -describe('validateLinksFieldOrThrow', () => { - describe('valid inputs', () => { - it('should return null when value is null', () => { - const result = validateLinksFieldOrThrow(null, 'testField'); - - expect(result).toBeNull(); - }); - - it('should return the links object when all fields are valid', () => { - const linksValue = { - primaryLinkUrl: 'https://example.com', - primaryLinkLabel: 'Example Website', - secondaryLinks: [{ url: 'https://secondary.com', label: 'Secondary' }], - }; - const result = validateLinksFieldOrThrow(linksValue, 'testField'); - - expect(result).toEqual(linksValue); - }); - - it('should return the links object when only primaryLinkUrl is provided', () => { - const linksValue = { - primaryLinkUrl: 'https://example.com', - }; - const result = validateLinksFieldOrThrow(linksValue, 'testField'); - - expect(result).toEqual(linksValue); - }); - }); - - describe('invalid inputs', () => { - it('should throw when value is not an object', () => { - expect(() => - validateLinksFieldOrThrow('not an object', 'testField'), - ).toThrow(CommonQueryRunnerException); - }); - - it('should throw when value is undefined', () => { - expect(() => validateLinksFieldOrThrow(undefined, 'testField')).toThrow( - CommonQueryRunnerException, - ); - }); - - it('should throw when primaryLinkUrl is not a string', () => { - const linksValue = { - primaryLinkUrl: 12345, - }; - - expect(() => validateLinksFieldOrThrow(linksValue, 'testField')).toThrow( - CommonQueryRunnerException, - ); - }); - - it('should throw when primaryLinkLabel is not a string', () => { - const linksValue = { - primaryLinkLabel: ['not', 'a', 'string'], - }; - - expect(() => validateLinksFieldOrThrow(linksValue, 'testField')).toThrow( - CommonQueryRunnerException, - ); - }); - - it('should throw when secondaryLinks is not an object or null', () => { - const linksValue = { - secondaryLinks: 'not an object', - }; - - expect(() => validateLinksFieldOrThrow(linksValue, 'testField')).toThrow( - CommonQueryRunnerException, - ); - }); - - it('should throw when invalid subfields are present', () => { - const linksValue = { - primaryLinkUrl: 'https://example.com', - invalidField1: 'invalid', - invalidField2: 'invalid', - }; - - expect(() => validateLinksFieldOrThrow(linksValue, 'testField')).toThrow( - CommonQueryRunnerException, - ); - }); - }); -}); diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-multi-select-field-or-throw.util.spec.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-multi-select-field-or-throw.util.spec.ts deleted file mode 100644 index 24ba546be0..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-multi-select-field-or-throw.util.spec.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { validateMultiSelectFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-multi-select-field-or-throw.util'; -import { CommonQueryRunnerException } from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception'; - -describe('validateMultiSelectFieldOrThrow', () => { - const validOptions = ['option1', 'option2', 'option3']; - - describe('valid inputs', () => { - it('should return null when value is null', () => { - const result = validateMultiSelectFieldOrThrow( - null, - 'testField', - validOptions, - ); - - expect(result).toBeNull(); - }); - - it('should return array when all values are in options', () => { - const value = ['option1', 'option2']; - const result = validateMultiSelectFieldOrThrow( - value, - 'testField', - validOptions, - ); - - expect(result).toEqual(value); - }); - - it('should return string when value is a single string in options', () => { - const value = 'option1'; - const result = validateMultiSelectFieldOrThrow( - value, - 'testField', - validOptions, - ); - - expect(result).toEqual(value); - }); - - it('should return empty array when value is empty array', () => { - const value: string[] = []; - const result = validateMultiSelectFieldOrThrow( - value, - 'testField', - validOptions, - ); - - expect(result).toEqual(value); - }); - }); - - describe('invalid inputs', () => { - it('should throw when options are undefined', () => { - expect(() => - validateMultiSelectFieldOrThrow(['option1'], 'testField', undefined), - ).toThrow(CommonQueryRunnerException); - }); - - it('should throw when options are not defined', () => { - expect(() => - validateMultiSelectFieldOrThrow(['option1'], 'testField'), - ).toThrow(CommonQueryRunnerException); - }); - - it('should throw when value contains option not in the options list', () => { - const value = ['option1', 'invalidOption']; - - expect(() => - validateMultiSelectFieldOrThrow(value, 'testField', validOptions), - ).toThrow(CommonQueryRunnerException); - }); - }); -}); diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-number-field-or-throw.util.spec.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-number-field-or-throw.util.spec.ts deleted file mode 100644 index a063a41b6b..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-number-field-or-throw.util.spec.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { validateNumberFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-number-field-or-throw.util'; - -describe('validateNumberFieldOrThrow', () => { - describe('valid inputs', () => { - it('should return null when value is null', () => { - const result = validateNumberFieldOrThrow(null, 'testField'); - - expect(result).toBeNull(); - }); - - it('should return the number when value is a positive integer', () => { - const result = validateNumberFieldOrThrow(123, 'testField'); - - expect(result).toBe(123); - }); - - it('should return the number when value is a negative integer', () => { - const result = validateNumberFieldOrThrow(-456, 'testField'); - - expect(result).toBe(-456); - }); - - it('should return the number when value is a positive float', () => { - const result = validateNumberFieldOrThrow(123.45, 'testField'); - - expect(result).toBe(123.45); - }); - - it('should return the number when value is zero', () => { - const result = validateNumberFieldOrThrow(0, 'testField'); - - expect(result).toBe(0); - }); - }); - - describe('invalid inputs', () => { - it('should throw when value is undefined', () => { - expect(() => validateNumberFieldOrThrow(undefined, 'testField')).toThrow( - 'Invalid number value undefined for field "testField"', - ); - }); - - it('should throw when value is a string with a number', () => { - expect(() => validateNumberFieldOrThrow('123', 'testField')).toThrow( - 'Invalid number value \'123\' for field "testField"', - ); - }); - - it('should throw when value is an empty string', () => { - expect(() => validateNumberFieldOrThrow('', 'testField')).toThrow( - 'Invalid number value \'\' for field "testField"', - ); - }); - - it('should throw when value is a boolean (true)', () => { - expect(() => validateNumberFieldOrThrow(true, 'testField')).toThrow( - 'Invalid number value true for field "testField"', - ); - }); - - it('should throw when value is a boolean (false)', () => { - expect(() => validateNumberFieldOrThrow(false, 'testField')).toThrow( - 'Invalid number value false for field "testField"', - ); - }); - - it('should throw when value is an array', () => { - expect(() => validateNumberFieldOrThrow([1, 2, 3], 'testField')).toThrow( - 'Invalid number value [ 1, 2, 3 ] for field "testField"', - ); - }); - - it('should throw when value is an object', () => { - expect(() => - validateNumberFieldOrThrow({ key: 'value' }, 'testField'), - ).toThrow( - 'Invalid number value { key: \'value\' } for field "testField"', - ); - }); - - it('should throw when value is NaN', () => { - expect(() => validateNumberFieldOrThrow(NaN, 'testField')).toThrow( - 'Invalid number value NaN for field "testField"', - ); - }); - }); -}); diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-numeric-field-or-throw.util.spec.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-numeric-field-or-throw.util.spec.ts deleted file mode 100644 index e2bd82a5c5..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-numeric-field-or-throw.util.spec.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { validateNumericFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-numeric-field-or-throw.util'; -import { CommonQueryRunnerException } from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception'; - -describe('validateNumericFieldOrThrow', () => { - describe('valid inputs', () => { - it('should return null when value is null', () => { - const result = validateNumericFieldOrThrow(null, 'testField'); - - expect(result).toBeNull(); - }); - - it('should return null when value is an empty string', () => { - const result = validateNumericFieldOrThrow('', 'testField'); - - expect(result).toBeNull(); - }); - - it('should return the number when value is a float', () => { - const result = validateNumericFieldOrThrow(3.14159, 'testField'); - - expect(result).toBe(3.14159); - }); - }); - describe('invalid inputs', () => { - it('should throw when value is NaN', () => { - expect(() => validateNumericFieldOrThrow(NaN, 'testField')).toThrow( - CommonQueryRunnerException, - ); - }); - - it('should throw when value is a non-numeric string', () => { - expect(() => - validateNumericFieldOrThrow('not a number', 'testField'), - ).toThrow(CommonQueryRunnerException); - }); - - it('should throw when value is undefined', () => { - expect(() => validateNumericFieldOrThrow(undefined, 'testField')).toThrow( - CommonQueryRunnerException, - ); - }); - - it('should throw when value is an array', () => { - expect(() => validateNumericFieldOrThrow([1, 2, 3], 'testField')).toThrow( - CommonQueryRunnerException, - ); - }); - }); -}); diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-phones-field-or-throw.util.spec.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-phones-field-or-throw.util.spec.ts deleted file mode 100644 index 745f5d0c76..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-phones-field-or-throw.util.spec.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { validatePhonesFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-phones-field-or-throw.util'; -import { CommonQueryRunnerException } from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception'; - -describe('validatePhonesFieldOrThrow', () => { - describe('valid inputs', () => { - it('should return null when value is null', () => { - const result = validatePhonesFieldOrThrow(null, 'testField'); - - expect(result).toBeNull(); - }); - - it('should return the phones object when all fields are valid', () => { - const phonesValue = { - primaryPhoneNumber: '+1234567890', - primaryPhoneCountryCode: 'US', - primaryPhoneCallingCode: '+1', - additionalPhones: null, - }; - const result = validatePhonesFieldOrThrow(phonesValue, 'testField'); - - expect(result).toEqual(phonesValue); - }); - - it('should return the phones object when only primaryPhoneNumber is provided', () => { - const phonesValue = { - primaryPhoneNumber: '+1234567890', - }; - const result = validatePhonesFieldOrThrow(phonesValue, 'testField'); - - expect(result).toEqual(phonesValue); - }); - }); - - describe('invalid inputs', () => { - it('should throw when value is not an object', () => { - expect(() => - validatePhonesFieldOrThrow('not an object', 'testField'), - ).toThrow(CommonQueryRunnerException); - }); - - it('should throw when value is undefined', () => { - expect(() => validatePhonesFieldOrThrow(undefined, 'testField')).toThrow( - CommonQueryRunnerException, - ); - }); - - it('should throw when primaryPhoneNumber is not a string', () => { - const phonesValue = { - primaryPhoneNumber: 123456, - }; - - expect(() => - validatePhonesFieldOrThrow(phonesValue, 'testField'), - ).toThrow(CommonQueryRunnerException); - }); - - it('should throw when primaryPhoneCountryCode is not a string', () => { - const phonesValue = { - primaryPhoneCountryCode: 123, - }; - - expect(() => - validatePhonesFieldOrThrow(phonesValue, 'testField'), - ).toThrow(CommonQueryRunnerException); - }); - - it('should throw when primaryPhoneCallingCode is not a string', () => { - const phonesValue = { - primaryPhoneCallingCode: 1, - }; - - expect(() => - validatePhonesFieldOrThrow(phonesValue, 'testField'), - ).toThrow(CommonQueryRunnerException); - }); - - it('should throw when an invalid subfield is present', () => { - const phonesValue = { - primaryPhoneNumber: '+1234567890', - invalidField: 'invalid', - }; - - expect(() => - validatePhonesFieldOrThrow(phonesValue, 'testField'), - ).toThrow('Invalid subfield invalidField for phones field "testField"'); - }); - }); -}); diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-rating-and-select-field-or-throw.util.spec.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-rating-and-select-field-or-throw.util.spec.ts deleted file mode 100644 index cab559bb3f..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-rating-and-select-field-or-throw.util.spec.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { validateRatingAndSelectFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-rating-and-select-field-or-throw.util'; - -describe('validateRatingAndSelectFieldOrThrow', () => { - const validOptions = ['option1', 'option2', 'option3']; - - describe('valid inputs', () => { - it('should return null when value is null', () => { - const result = validateRatingAndSelectFieldOrThrow( - null, - 'testField', - validOptions, - ); - - expect(result).toBeNull(); - }); - - it('should return the string when value is a valid option', () => { - const result = validateRatingAndSelectFieldOrThrow( - 'option1', - 'testField', - validOptions, - ); - - expect(result).toBe('option1'); - }); - }); - - describe('invalid inputs', () => { - it('should throw when options is undefined', () => { - expect(() => - validateRatingAndSelectFieldOrThrow('option1', 'testField', undefined), - ).toThrow('Invalid options for field "testField"'); - }); - - it('should throw when value is not in the options list', () => { - expect(() => - validateRatingAndSelectFieldOrThrow( - 'invalidOption', - 'testField', - validOptions, - ), - ).toThrow('Invalid value \'invalidOption\' for field "testField"'); - }); - - it('should throw when value is a number', () => { - expect(() => - validateRatingAndSelectFieldOrThrow(123, 'testField', validOptions), - ).toThrow('Invalid string value 123 for text field "testField"'); - }); - }); -}); diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-raw-json-field-or-throw.util.spec.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-raw-json-field-or-throw.util.spec.ts deleted file mode 100644 index 8ace0f960e..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-raw-json-field-or-throw.util.spec.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { validateRawJsonFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-raw-json-field-or-throw.util'; -import { CommonQueryRunnerException } from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception'; - -describe('validateRawJsonFieldOrThrow', () => { - describe('valid inputs', () => { - it('should return null when value is null', () => { - const result = validateRawJsonFieldOrThrow(null, 'testField'); - - expect(result).toBeNull(); - }); - - it('should return empty object when value is an empty object', () => { - const result = validateRawJsonFieldOrThrow({}, 'testField'); - - expect(result).toEqual({}); - }); - - it('should return the value when it is a valid JSON object', () => { - const jsonObject = { key: 'value', nested: { prop: 123 } }; - const result = validateRawJsonFieldOrThrow(jsonObject, 'testField'); - - expect(result).toEqual(jsonObject); - }); - - it('should return the value when it is a valid JSON array', () => { - const jsonArray = [1, 2, 3, 'test', { key: 'value' }]; - const result = validateRawJsonFieldOrThrow(jsonArray, 'testField'); - - expect(result).toEqual(jsonArray); - }); - }); - - describe('invalid inputs', () => { - it('should throw when value is undefined', () => { - expect(() => validateRawJsonFieldOrThrow(undefined, 'testField')).toThrow( - CommonQueryRunnerException, - ); - }); - - it('should throw when value is a function', () => { - const functionValue = () => 'test'; - - expect(() => - validateRawJsonFieldOrThrow(functionValue, 'testField'), - ).toThrow(CommonQueryRunnerException); - }); - - it('should throw when value is a number', () => { - expect(() => validateRawJsonFieldOrThrow(42, 'testField')).toThrow( - CommonQueryRunnerException, - ); - }); - - it('should throw when value is a string', () => { - expect(() => - validateRawJsonFieldOrThrow('string value', 'testField'), - ).toThrow(CommonQueryRunnerException); - }); - }); -}); diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-rich-text-v2-field-or-throw.util.spec.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-rich-text-v2-field-or-throw.util.spec.ts deleted file mode 100644 index 008aed8c33..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-rich-text-v2-field-or-throw.util.spec.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { validateRichTextV2FieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-rich-text-v2-field-or-throw.util'; -import { CommonQueryRunnerException } from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception'; - -describe('validateRichTextV2FieldOrThrow', () => { - describe('valid inputs', () => { - it('should return null when value is null', () => { - const result = validateRichTextV2FieldOrThrow(null, 'testField'); - - expect(result).toBeNull(); - }); - - it('should return null when value is an empty object', () => { - const result = validateRichTextV2FieldOrThrow({}, 'testField'); - - expect(result).toBeNull(); - }); - - it('should return the value when it has valid subfields', () => { - const value = { - blocknote: 'some blocknote content', - markdown: '# Heading\nContent', - }; - const result = validateRichTextV2FieldOrThrow(value, 'testField'); - - expect(result).toEqual(value); - }); - }); - - describe('invalid inputs', () => { - it('should throw when value is undefined', () => { - expect(() => - validateRichTextV2FieldOrThrow(undefined, 'testField'), - ).toThrow(CommonQueryRunnerException); - }); - - it('should throw when value is a string', () => { - expect(() => - validateRichTextV2FieldOrThrow('not an object', 'testField'), - ).toThrow(CommonQueryRunnerException); - }); - - it('should throw when value has invalid subfields', () => { - const value = { invalidField: 'value' }; - - expect(() => validateRichTextV2FieldOrThrow(value, 'testField')).toThrow( - CommonQueryRunnerException, - ); - expect(() => validateRichTextV2FieldOrThrow(value, 'testField')).toThrow( - /Should have only blocknote, markdown subfields/, - ); - }); - }); -}); diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-text-field-or-throw.util.spec.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-text-field-or-throw.util.spec.ts deleted file mode 100644 index f5f4ffef8f..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-text-field-or-throw.util.spec.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { validateTextFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-text-field-or-throw.util'; - -describe('validateTextFieldOrThrow', () => { - describe('valid inputs', () => { - it('should return null when value is null', () => { - const result = validateTextFieldOrThrow(null, 'testField'); - - expect(result).toBeNull(); - }); - - it('should return empty string when value is an empty string', () => { - const result = validateTextFieldOrThrow('', 'testField'); - - expect(result).toEqual(''); - }); - - it('should return the string when value is a regular string', () => { - const result = validateTextFieldOrThrow('hello world', 'testField'); - - expect(result).toBe('hello world'); - }); - }); - - describe('invalid inputs', () => { - it('should throw when value is undefined', () => { - expect(() => validateTextFieldOrThrow(undefined, 'testField')).toThrow( - 'Invalid string value undefined for text field "testField"', - ); - }); - - it('should throw when value is a number', () => { - expect(() => validateTextFieldOrThrow(123, 'testField')).toThrow( - 'Invalid string value 123 for text field "testField"', - ); - }); - - it('should throw when value is a float number', () => { - expect(() => validateTextFieldOrThrow(123.45, 'testField')).toThrow( - 'Invalid string value 123.45 for text field "testField"', - ); - }); - - it('should throw when value is a boolean (true)', () => { - expect(() => validateTextFieldOrThrow(true, 'testField')).toThrow( - 'Invalid string value true for text field "testField"', - ); - }); - - it('should throw when value is a boolean (false)', () => { - expect(() => validateTextFieldOrThrow(false, 'testField')).toThrow( - 'Invalid string value false for text field "testField"', - ); - }); - - it('should throw when value is an array', () => { - expect(() => validateTextFieldOrThrow([1, 2, 3], 'testField')).toThrow( - 'Invalid string value [ 1, 2, 3 ] for text field "testField"', - ); - }); - - it('should throw when value is an object', () => { - expect(() => - validateTextFieldOrThrow({ key: 'value' }, 'testField'), - ).toThrow( - 'Invalid string value { key: \'value\' } for text field "testField"', - ); - }); - }); -}); diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-uuid-field-or-throw.util.spec.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-uuid-field-or-throw.util.spec.ts deleted file mode 100644 index aff37b6296..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/__tests__/validate-uuid-field-or-throw.util.spec.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { validateUUIDFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-uuid-field-or-throw.util'; -import { CommonQueryRunnerException } from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception'; - -describe('validateUUIDFieldOrThrow', () => { - describe('valid inputs', () => { - it('should return null when value is null', () => { - const result = validateUUIDFieldOrThrow(null, 'testField'); - - expect(result).toBeNull(); - }); - - it('should return the value when it is a valid UUID v4', () => { - const validUuid = '550e8400-e29b-41d4-a716-446655440000'; - const result = validateUUIDFieldOrThrow(validUuid, 'testField'); - - expect(result).toBe(validUuid); - }); - }); - - describe('invalid inputs', () => { - it('should throw when value is undefined', () => { - expect(() => validateUUIDFieldOrThrow(undefined, 'testField')).toThrow( - CommonQueryRunnerException, - ); - }); - - it('should throw when value is an empty string', () => { - expect(() => validateUUIDFieldOrThrow('', 'testField')).toThrow( - CommonQueryRunnerException, - ); - }); - - it('should throw when value is an invalid UUID format', () => { - expect(() => - validateUUIDFieldOrThrow('invalid-uuid', 'testField'), - ).toThrow(CommonQueryRunnerException); - }); - }); -}); diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-actor-field-or-throw.util.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-actor-field-or-throw.util.ts deleted file mode 100644 index b4d63aab5d..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-actor-field-or-throw.util.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { isNull } from '@sniptt/guards'; -import { FieldActorSource } from 'twenty-shared/types'; - -import { validateRatingAndSelectFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-rating-and-select-field-or-throw.util'; -import { validateRawJsonFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-raw-json-field-or-throw.util'; -import { - CommonQueryRunnerException, - CommonQueryRunnerExceptionCode, -} from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception'; - -export const validateActorFieldOrThrow = ( - value: unknown, - fieldName: string, -): { source: FieldActorSource; context: Record } | null => { - const preValidatedValue = validateRawJsonFieldOrThrow(value, fieldName); - - if (isNull(preValidatedValue)) return null; - - for (const [subField, subFieldValue] of Object.entries(preValidatedValue)) { - switch (subField) { - case 'source': - validateRatingAndSelectFieldOrThrow( - subFieldValue, - `${fieldName}.${subField}`, - Object.keys(FieldActorSource), - ); - break; - case 'context': - validateRawJsonFieldOrThrow(subFieldValue, `${fieldName}.${subField}`); - break; - default: - throw new CommonQueryRunnerException( - `Invalid subfield ${subField} for actor field "${fieldName}"`, - CommonQueryRunnerExceptionCode.INVALID_ARGS_DATA, - ); - } - } - - return value as { - source: FieldActorSource; - context: Record; - }; -}; diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-address-field-or-throw.util.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-address-field-or-throw.util.ts deleted file mode 100644 index bc1fbcb3a1..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-address-field-or-throw.util.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { isNull } from '@sniptt/guards'; - -import { validateNumericFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-numeric-field-or-throw.util'; -import { validateRawJsonFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-raw-json-field-or-throw.util'; -import { validateTextFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-text-field-or-throw.util'; -import { - CommonQueryRunnerException, - CommonQueryRunnerExceptionCode, -} from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception'; - -export const validateAddressFieldOrThrow = ( - value: unknown, - fieldName: string, -): { - addressStreet1?: string | null; - addressStreet2?: string | null; - addressCity?: string | null; - addressState?: string | null; - addressPostcode?: string | null; - addressCountry?: string | null; - addressLat?: number | null; - addressLng?: number | null; -} | null => { - const preValidatedValue = validateRawJsonFieldOrThrow(value, fieldName); - - if (isNull(preValidatedValue)) return null; - - for (const [subField, subFieldValue] of Object.entries(preValidatedValue)) { - switch (subField) { - case 'addressStreet1': - validateTextFieldOrThrow(subFieldValue, `${fieldName}.${subField}`); - break; - case 'addressStreet2': - validateTextFieldOrThrow(subFieldValue, `${fieldName}.${subField}`); - break; - case 'addressCity': - validateTextFieldOrThrow(subFieldValue, `${fieldName}.${subField}`); - break; - case 'addressState': - validateTextFieldOrThrow(subFieldValue, `${fieldName}.${subField}`); - break; - case 'addressPostcode': - validateTextFieldOrThrow(subFieldValue, `${fieldName}.${subField}`); - break; - case 'addressCountry': - validateTextFieldOrThrow(subFieldValue, `${fieldName}.${subField}`); - break; - case 'addressLat': - validateNumericFieldOrThrow(subFieldValue, `${fieldName}.${subField}`); - break; - case 'addressLng': - validateNumericFieldOrThrow(subFieldValue, `${fieldName}.${subField}`); - break; - default: - throw new CommonQueryRunnerException( - `Invalid subfield ${subField} for address field "${fieldName}"`, - CommonQueryRunnerExceptionCode.INVALID_ARGS_DATA, - ); - } - } - - return value as { - addressStreet1?: string | null; - addressStreet2?: string | null; - addressCity?: string | null; - addressState?: string | null; - addressPostcode?: string | null; - addressCountry?: string | null; - addressLat?: number | null; - addressLng?: number | null; - }; -}; diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-array-field-or-throw.util.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-array-field-or-throw.util.ts deleted file mode 100644 index d89b59e1d7..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-array-field-or-throw.util.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { inspect } from 'util'; - -import { isNull } from '@sniptt/guards'; - -import { - CommonQueryRunnerException, - CommonQueryRunnerExceptionCode, -} from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception'; - -export const validateArrayFieldOrThrow = ( - value: unknown, - fieldName: string, -): string | string[] | null => { - if (isNull(value)) return null; - - if (typeof value === 'string') return value; - - if (!Array.isArray(value) || value.some((item) => typeof item !== 'string')) { - throw new CommonQueryRunnerException( - `Invalid value ${inspect(value)} for field "${fieldName} - Array values need to be string"`, - CommonQueryRunnerExceptionCode.INVALID_ARGS_DATA, - ); - } - - return value; -}; diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-boolean-field-or-throw.util.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-boolean-field-or-throw.util.ts deleted file mode 100644 index 2d091fe2e0..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-boolean-field-or-throw.util.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { inspect } from 'util'; - -import { isNull } from '@sniptt/guards'; - -import { - CommonQueryRunnerException, - CommonQueryRunnerExceptionCode, -} from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception'; - -export const validateBooleanFieldOrThrow = ( - value: unknown, - fieldName: string, -): boolean | null => { - if (typeof value !== 'boolean' && !isNull(value)) - throw new CommonQueryRunnerException( - `Invalid boolean value ${inspect(value)} for field "${fieldName}"`, - CommonQueryRunnerExceptionCode.INVALID_ARGS_DATA, - ); - - return value; -}; diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-currency-field-or-throw.util.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-currency-field-or-throw.util.ts deleted file mode 100644 index a5f518ba71..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-currency-field-or-throw.util.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { isNull } from '@sniptt/guards'; - -import { validateNumericFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-numeric-field-or-throw.util'; -import { validateRawJsonFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-raw-json-field-or-throw.util'; -import { validateTextFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-text-field-or-throw.util'; -import { - CommonQueryRunnerException, - CommonQueryRunnerExceptionCode, -} from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception'; - -export const validateCurrencyFieldOrThrow = ( - value: unknown, - fieldName: string, -): { - amountMicros?: number | string | null; - currencyCode?: string | null; -} | null => { - const preValidatedValue = validateRawJsonFieldOrThrow(value, fieldName); - - if (isNull(preValidatedValue)) return null; - - for (const [subField, subFieldValue] of Object.entries(preValidatedValue)) { - switch (subField) { - case 'amountMicros': - validateNumericFieldOrThrow(subFieldValue, `${fieldName}.${subField}`); - break; - case 'currencyCode': - validateTextFieldOrThrow(subFieldValue, `${fieldName}.${subField}`); - break; - - default: - throw new CommonQueryRunnerException( - `Invalid subfield ${subField} for currency field "${fieldName}"`, - CommonQueryRunnerExceptionCode.INVALID_ARGS_DATA, - ); - } - } - - return value as { - amountMicros?: number | string | null; - currencyCode?: string | null; - }; -}; diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-date-and-date-time-field-or-throw.util.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-date-and-date-time-field-or-throw.util.ts deleted file mode 100644 index a4a39c3733..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-date-and-date-time-field-or-throw.util.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { inspect } from 'util'; - -import { isDate, isNull, isNumber, isString } from '@sniptt/guards'; - -import { - CommonQueryRunnerException, - CommonQueryRunnerExceptionCode, -} from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception'; - -export const validateDateAndDateTimeFieldOrThrow = ( - value: unknown, - fieldName: string, -) => { - if (isNull(value)) return null; - - if (isString(value) || isNumber(value) || isDate(value)) { - const date = new Date(value); - - if (!isNaN(date.getTime())) return value; - } - - throw new CommonQueryRunnerException( - `Invalid value ${inspect(value)} for date or date-time field "${fieldName}"`, - CommonQueryRunnerExceptionCode.INVALID_ARGS_DATA, - ); -}; diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-emails-field-or-throw.util.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-emails-field-or-throw.util.ts deleted file mode 100644 index 32b9e5597a..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-emails-field-or-throw.util.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { isNull } from '@sniptt/guards'; - -import { validateArrayFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-array-field-or-throw.util'; -import { validateRawJsonFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-raw-json-field-or-throw.util'; -import { validateTextFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-text-field-or-throw.util'; -import { - CommonQueryRunnerException, - CommonQueryRunnerExceptionCode, -} from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception'; - -export const validateEmailsFieldOrThrow = ( - value: unknown, - fieldName: string, -): { - primaryEmail?: string | null; - additionalEmails?: string[] | null; -} | null => { - const preValidatedValue = validateRawJsonFieldOrThrow(value, fieldName); - - if (isNull(preValidatedValue)) return null; - - for (const [subField, subFieldValue] of Object.entries(preValidatedValue)) { - switch (subField) { - case 'primaryEmail': - validateTextFieldOrThrow(subFieldValue, `${fieldName}.${subField}`); - break; - case 'additionalEmails': - validateArrayFieldOrThrow(subFieldValue, `${fieldName}.${subField}`); - break; - default: - throw new CommonQueryRunnerException( - `Invalid subfield ${subField} for emails field "${fieldName}"`, - CommonQueryRunnerExceptionCode.INVALID_ARGS_DATA, - ); - } - } - - return value as { - primaryEmail?: string | null; - additionalEmails?: string[] | null; - }; -}; diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-full-name-field-or-throw.util.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-full-name-field-or-throw.util.ts deleted file mode 100644 index cc02d87cd3..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-full-name-field-or-throw.util.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { isNull } from '@sniptt/guards'; - -import { validateRawJsonFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-raw-json-field-or-throw.util'; -import { validateTextFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-text-field-or-throw.util'; -import { - CommonQueryRunnerException, - CommonQueryRunnerExceptionCode, -} from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception'; - -export const validateFullNameFieldOrThrow = ( - value: unknown, - fieldName: string, -): { - firstName?: string | null; - lastName?: string | null; -} | null => { - const preValidatedValue = validateRawJsonFieldOrThrow(value, fieldName); - - if (isNull(preValidatedValue)) return null; - - for (const [subField, subFieldValue] of Object.entries(preValidatedValue)) { - switch (subField) { - case 'firstName': - validateTextFieldOrThrow(subFieldValue, `${fieldName}.${subField}`); - break; - case 'lastName': - validateTextFieldOrThrow(subFieldValue, `${fieldName}.${subField}`); - break; - default: - throw new CommonQueryRunnerException( - `Invalid subfield ${subField} for full name field "${fieldName}"`, - CommonQueryRunnerExceptionCode.INVALID_ARGS_DATA, - ); - } - } - - return value as { - firstName?: string | null; - lastName?: string | null; - }; -}; diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-links-field-or-throw.util.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-links-field-or-throw.util.ts deleted file mode 100644 index e0d7efd684..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-links-field-or-throw.util.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { isNull } from '@sniptt/guards'; - -import { validateRawJsonFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-raw-json-field-or-throw.util'; -import { validateTextFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-text-field-or-throw.util'; -import { - CommonQueryRunnerException, - CommonQueryRunnerExceptionCode, -} from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception'; -import { type LinksFieldGraphQLInput } from 'src/engine/core-modules/record-transformer/utils/transform-links-value.util'; - -export const validateLinksFieldOrThrow = ( - value: unknown, - fieldName: string, -): LinksFieldGraphQLInput | null => { - const preValidatedValue = validateRawJsonFieldOrThrow(value, fieldName); - - if (isNull(preValidatedValue)) return null; - - for (const [subField, subFieldValue] of Object.entries(preValidatedValue)) { - switch (subField) { - case 'primaryLinkUrl': - validateTextFieldOrThrow(subFieldValue, `${fieldName}.${subField}`); - break; - case 'primaryLinkLabel': - validateTextFieldOrThrow(subFieldValue, `${fieldName}.${subField}`); - break; - case 'secondaryLinks': - validateRawJsonFieldOrThrow(subFieldValue, `${fieldName}.${subField}`); - break; - default: - throw new CommonQueryRunnerException( - `Invalid subfield ${subField} for links field "${fieldName}"`, - CommonQueryRunnerExceptionCode.INVALID_ARGS_DATA, - ); - } - } - - return value as LinksFieldGraphQLInput; -}; diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-multi-select-field-or-throw.util.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-multi-select-field-or-throw.util.ts deleted file mode 100644 index 6c263ecf80..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-multi-select-field-or-throw.util.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { inspect } from 'util'; - -import { isNull } from '@sniptt/guards'; -import { isDefined } from 'twenty-shared/utils'; - -import { validateArrayFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-array-field-or-throw.util'; -import { - CommonQueryRunnerException, - CommonQueryRunnerExceptionCode, -} from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception'; - -export const validateMultiSelectFieldOrThrow = ( - value: unknown, - fieldName: string, - options?: string[], -): string | string[] | null => { - const preValidatedValue = validateArrayFieldOrThrow(value, fieldName); - - if (isNull(preValidatedValue)) return null; - - if (!isDefined(options)) { - throw new CommonQueryRunnerException( - `Invalid options for field "${fieldName}"`, - CommonQueryRunnerExceptionCode.INVALID_ARGS_DATA, - ); - } - - if ( - (Array.isArray(preValidatedValue) - ? preValidatedValue - : [preValidatedValue] - ).some((item) => !options.includes(item)) - ) { - throw new CommonQueryRunnerException( - `Invalid value ${inspect(value)} for multi select field "${fieldName}"`, - CommonQueryRunnerExceptionCode.INVALID_ARGS_DATA, - ); - } - - return value as string | string[]; -}; diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-number-field-or-throw.util.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-number-field-or-throw.util.ts deleted file mode 100644 index a56e78d0f7..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-number-field-or-throw.util.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { inspect } from 'util'; - -import { isNull } from '@sniptt/guards'; - -import { - CommonQueryRunnerException, - CommonQueryRunnerExceptionCode, -} from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception'; - -export const validateNumberFieldOrThrow = ( - value: unknown, - fieldName: string, -): number | null => { - if ( - (typeof value !== 'number' && !isNull(value)) || - (typeof value === 'number' && - (isNaN(value) || value === Infinity || value === -Infinity)) - ) - throw new CommonQueryRunnerException( - `Invalid number value ${inspect(value)} for field "${fieldName}"`, - CommonQueryRunnerExceptionCode.INVALID_ARGS_DATA, - ); - - return value; -}; diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-numeric-field-or-throw.util.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-numeric-field-or-throw.util.ts deleted file mode 100644 index f4abfea14e..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-numeric-field-or-throw.util.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { isNull } from '@sniptt/guards'; - -import { validateNumberFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-number-field-or-throw.util'; - -//Need to handle stringified numbers because of BigFloatScalarType custom gql type - -export const validateNumericFieldOrThrow = ( - value: unknown, - fieldName: string, -): number | string | null => { - if (value === '' || isNull(value)) return null; - - const numberValue = Number(value); - - validateNumberFieldOrThrow(numberValue, fieldName); - - return value as number | string; -}; diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-phones-field-or-throw.util.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-phones-field-or-throw.util.ts deleted file mode 100644 index 2a3017739a..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-phones-field-or-throw.util.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { isNull } from '@sniptt/guards'; - -import { validateRawJsonFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-raw-json-field-or-throw.util'; -import { validateTextFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-text-field-or-throw.util'; -import { - CommonQueryRunnerException, - CommonQueryRunnerExceptionCode, -} from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception'; -import { type PhonesFieldGraphQLInput } from 'src/engine/core-modules/record-transformer/utils/transform-phones-value.util'; - -export const validatePhonesFieldOrThrow = ( - value: unknown, - fieldName: string, -): PhonesFieldGraphQLInput => { - const preValidatedValue = validateRawJsonFieldOrThrow(value, fieldName); - - if (isNull(preValidatedValue)) return null; - - for (const [subField, subFieldValue] of Object.entries(preValidatedValue)) { - switch (subField) { - case 'primaryPhoneNumber': - validateTextFieldOrThrow(subFieldValue, `${fieldName}.${subField}`); - break; - case 'primaryPhoneCountryCode': - validateTextFieldOrThrow(subFieldValue, `${fieldName}.${subField}`); - break; - case 'primaryPhoneCallingCode': - validateTextFieldOrThrow(subFieldValue, `${fieldName}.${subField}`); - break; - case 'additionalPhones': - validateRawJsonFieldOrThrow(subFieldValue, `${fieldName}.${subField}`); - break; - - default: - throw new CommonQueryRunnerException( - `Invalid subfield ${subField} for phones field "${fieldName}"`, - CommonQueryRunnerExceptionCode.INVALID_ARGS_DATA, - ); - } - } - - return value as PhonesFieldGraphQLInput; -}; diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-rating-and-select-field-or-throw.util.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-rating-and-select-field-or-throw.util.ts deleted file mode 100644 index f47b134f08..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-rating-and-select-field-or-throw.util.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { inspect } from 'util'; - -import { isNull } from '@sniptt/guards'; -import { isDefined } from 'twenty-shared/utils'; - -import { validateTextFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-text-field-or-throw.util'; -import { - CommonQueryRunnerException, - CommonQueryRunnerExceptionCode, -} from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception'; - -export const validateRatingAndSelectFieldOrThrow = ( - value: unknown, - fieldName: string, - options?: string[], -): string | null => { - const preValidatedValue = validateTextFieldOrThrow(value, fieldName); - - if (!isDefined(options)) { - throw new CommonQueryRunnerException( - `Invalid options for field "${fieldName}"`, - CommonQueryRunnerExceptionCode.INVALID_ARGS_DATA, - ); - } - - if (!isNull(preValidatedValue) && !options.includes(preValidatedValue)) { - throw new CommonQueryRunnerException( - `Invalid value ${inspect(value)} for field "${fieldName}"`, - CommonQueryRunnerExceptionCode.INVALID_ARGS_DATA, - ); - } - - return preValidatedValue; -}; diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-raw-json-field-or-throw.util.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-raw-json-field-or-throw.util.ts deleted file mode 100644 index 04dd0af108..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-raw-json-field-or-throw.util.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { inspect } from 'util'; - -import { isNull, isObject } from '@sniptt/guards'; - -import { - CommonQueryRunnerException, - CommonQueryRunnerExceptionCode, -} from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception'; - -export const validateRawJsonFieldOrThrow = ( - value: unknown, - fieldName: string, -): object | null => { - if (isNull(value)) return null; - - if (!isObject(value)) { - throw new CommonQueryRunnerException( - `Invalid object value ${inspect(value)} for field "${fieldName}"`, - CommonQueryRunnerExceptionCode.INVALID_ARGS_DATA, - ); - } - - return value; -}; diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-rich-text-v2-field-or-throw.util.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-rich-text-v2-field-or-throw.util.ts deleted file mode 100644 index 425aad4a5c..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-rich-text-v2-field-or-throw.util.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { inspect } from 'util'; - -import { isNull, isObject } from '@sniptt/guards'; -import { - compositeTypeDefinitions, - FieldMetadataType, -} from 'twenty-shared/types'; - -import { - CommonQueryRunnerException, - CommonQueryRunnerExceptionCode, -} from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception'; - -export const validateRichTextV2FieldOrThrow = ( - value: unknown, - fieldName: string, -): { - blocknote: string | null | undefined; - markdown: string | null | undefined; -} | null => { - if (isNull(value)) return null; - - try { - const parsedValue = JSON.parse(JSON.stringify(value)); - - if (!isObject(parsedValue)) throw new Error('Should be an object'); - - if (Object.keys(parsedValue).length === 0) return null; - - const subfields = Object.keys(parsedValue); - const richTextV2Subfields = compositeTypeDefinitions - .get(FieldMetadataType.RICH_TEXT_V2) - ?.properties.filter( - (prop) => prop.hidden !== true && prop.hidden !== 'input', - ) - .map((prop) => prop.name); - - if (!subfields.every((subfield) => richTextV2Subfields?.includes(subfield))) - throw new Error( - `Should have only ${richTextV2Subfields?.join(', ')} subfields`, - ); - - return value as { - blocknote: string | null | undefined; - markdown: string | null | undefined; - }; - } catch (error) { - throw new CommonQueryRunnerException( - `Invalid rich text v2 value ${inspect(value)} for field "${fieldName}" - ${error.message}`, - CommonQueryRunnerExceptionCode.INVALID_ARGS_DATA, - ); - } -}; diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-text-field-or-throw.util.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-text-field-or-throw.util.ts deleted file mode 100644 index f39f8ec6df..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-text-field-or-throw.util.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { inspect } from 'util'; - -import { isNull } from '@sniptt/guards'; - -import { - CommonQueryRunnerException, - CommonQueryRunnerExceptionCode, -} from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception'; - -export const validateTextFieldOrThrow = ( - value: unknown, - fieldName: string, -): string | null => { - if (typeof value !== 'string' && !isNull(value)) - throw new CommonQueryRunnerException( - `Invalid string value ${inspect(value)} for text field "${fieldName}"`, - CommonQueryRunnerExceptionCode.INVALID_ARGS_DATA, - ); - - return value; -}; diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-uuid-field-or-throw.util.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-uuid-field-or-throw.util.ts deleted file mode 100644 index 00e0094215..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-uuid-field-or-throw.util.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { inspect } from 'util'; - -import { isNull } from '@sniptt/guards'; -import { isValidUuid } from 'twenty-shared/utils'; - -import { - CommonQueryRunnerException, - CommonQueryRunnerExceptionCode, -} from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception'; - -export const validateUUIDFieldOrThrow = ( - value: unknown, - fieldName: string, -): string | null => { - if (!isValidUuid(value as string) && !isNull(value)) - throw new CommonQueryRunnerException( - `Invalid UUID value ${inspect(value)} for field "${fieldName}"`, - CommonQueryRunnerExceptionCode.INVALID_ARGS_DATA, - ); - - return value as string; -}; diff --git a/packages/twenty-server/src/engine/api/common/common-args-processors/query-runner-args.factory.ts b/packages/twenty-server/src/engine/api/common/common-args-processors/query-runner-args.factory.ts deleted file mode 100644 index 2af321989f..0000000000 --- a/packages/twenty-server/src/engine/api/common/common-args-processors/query-runner-args.factory.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { Injectable } from '@nestjs/common'; - -import { FieldMetadataType } from 'twenty-shared/types'; -import { isDefined } from 'twenty-shared/utils'; - -import { type ObjectRecordFilter } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface'; - -import { RecordPositionService } from 'src/engine/core-modules/record-position/services/record-position.service'; -import { RecordInputTransformerService } from 'src/engine/core-modules/record-transformer/services/record-input-transformer.service'; -import { type FieldMetadataMap } from 'src/engine/metadata-modules/types/field-metadata-map'; -import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps'; - -@Injectable() -export class QueryRunnerArgsFactory { - constructor( - private readonly recordPositionService: RecordPositionService, - private readonly recordInputTransformerService: RecordInputTransformerService, - ) {} - - public overrideFilterByFieldMetadata< - T extends ObjectRecordFilter | undefined, - >( - filter: T, - objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps, - ): T { - if (!isDefined(filter)) { - return filter; - } - - const overrideFilter = (filterObject: ObjectRecordFilter) => { - return Object.entries(filterObject).reduce((acc, [key, value]) => { - if (key === 'and' || key === 'or') { - // @ts-expect-error legacy noImplicitAny - acc[key] = value.map((nestedFilter: ObjectRecordFilter) => - overrideFilter(nestedFilter), - ); - } else if (key === 'not') { - // @ts-expect-error legacy noImplicitAny - acc[key] = overrideFilter(value); - } else { - // @ts-expect-error legacy noImplicitAny - acc[key] = this.transformFilterValueByType( - key, - value, - objectMetadataItemWithFieldMaps, - ); - } - - return acc; - }, {}); - }; - - return overrideFilter(filter) as T; - } - - private transformFilterValueByType( - key: string, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - value: any, - objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps, - ) { - const fieldMetadataId = objectMetadataItemWithFieldMaps.fieldIdByName[key]; - const fieldMetadata = - objectMetadataItemWithFieldMaps.fieldsById[fieldMetadataId]; - - if (!fieldMetadata) { - return value; - } - - // Special handling for filter values, which have a specific structure - switch (fieldMetadata.type) { - case FieldMetadataType.NUMBER: { - if (value?.is === 'NULL') { - return value; - } else { - return Object.fromEntries( - Object.entries(value).map(([filterKey, filterValue]) => [ - filterKey, - Number(filterValue), - ]), - ); - } - } - default: - return value; - } - } - - async overrideValueByFieldMetadata( - key: string, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - value: any, - fieldMetadataMapByName: FieldMetadataMap, - objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps, - ) { - const fieldMetadata = fieldMetadataMapByName[key]; - - if (!fieldMetadata) { - return value; - } - - return this.recordInputTransformerService.process({ - recordInput: { [key]: value }, - objectMetadataMapItem: objectMetadataItemWithFieldMaps, - }); - } -} diff --git a/packages/twenty-server/src/engine/api/common/common-query-runners/common-base-query-runner.service.ts b/packages/twenty-server/src/engine/api/common/common-query-runners/common-base-query-runner.service.ts index 92d039d704..80915d37d5 100644 --- a/packages/twenty-server/src/engine/api/common/common-query-runners/common-base-query-runner.service.ts +++ b/packages/twenty-server/src/engine/api/common/common-query-runners/common-base-query-runner.service.ts @@ -6,8 +6,7 @@ import { Omit } from 'zod/v4/core/util.cjs'; import { WorkspaceAuthContext } from 'src/engine/api/common/interfaces/workspace-auth-context.interface'; import { QueryResultFieldValue } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/interfaces/query-result-field-value'; -import { DataArgProcessor } from 'src/engine/api/common/common-args-processors/data-arg-processor/data-arg.processor'; -import { QueryRunnerArgsFactory } from 'src/engine/api/common/common-args-processors/query-runner-args.factory'; +import { CommonSelectedFieldsHandler } from 'src/engine/api/common/common-args-handlers/common-query-selected-fields/common-selected-fields.handler'; import { CommonQueryRunnerException, CommonQueryRunnerExceptionCode, @@ -26,6 +25,8 @@ import { isWorkspaceAuthContext } from 'src/engine/api/common/utils/is-workspace import { OBJECTS_WITH_SETTINGS_PERMISSIONS_REQUIREMENTS } from 'src/engine/api/graphql/graphql-query-runner/constants/objects-with-settings-permissions-requirements'; import { GraphqlQueryParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query.parser'; import { ProcessNestedRelationsHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/process-nested-relations.helper'; +import { QueryResultGettersFactory } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/query-result-getters.factory'; +import { QueryRunnerArgsFactory } from 'src/engine/api/graphql/workspace-query-runner/factories/query-runner-args.factory'; import { WorkspacePreQueryHookPayload } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/types/workspace-query-hook.type'; import { WorkspaceQueryHookService } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/workspace-query-hook.service'; import { ApiKeyRoleService } from 'src/engine/core-modules/api-key/api-key-role.service'; @@ -61,7 +62,7 @@ export abstract class CommonBaseQueryRunnerService< @Inject() protected readonly queryRunnerArgsFactory: QueryRunnerArgsFactory; @Inject() - protected readonly dataArgProcessor: DataArgProcessor; + protected readonly queryResultGettersFactory: QueryResultGettersFactory; @Inject() protected readonly twentyORMGlobalManager: TwentyORMGlobalManager; @Inject() @@ -75,6 +76,8 @@ export abstract class CommonBaseQueryRunnerService< @Inject() protected readonly apiKeyRoleService: ApiKeyRoleService; @Inject() + protected readonly selectedFieldsHandler: CommonSelectedFieldsHandler; + @Inject() protected readonly workspacePermissionsCacheService: WorkspacePermissionsCacheService; @Inject() protected readonly commonResultGettersService: CommonResultGettersService; @@ -188,19 +191,18 @@ export abstract class CommonBaseQueryRunnerService< ); const { authContext, objectMetadataItemWithFieldMaps } = queryRunnerContext; - - const computedArgs = await this.computeArgs(args, queryRunnerContext); - const hookedArgs = (await this.workspaceQueryHookService.executePreQueryHooks( authContext, objectMetadataItemWithFieldMaps.nameSingular, operationName, - computedArgs as WorkspacePreQueryHookPayload, + args as WorkspacePreQueryHookPayload, )) as CommonInput; + const computedArgs = await this.computeArgs(hookedArgs, queryRunnerContext); + return { - ...hookedArgs, + ...computedArgs, selectedFieldsResult, }; } diff --git a/packages/twenty-server/src/engine/api/common/common-query-runners/common-create-many-query-runner/common-create-many-query-runner.service.ts b/packages/twenty-server/src/engine/api/common/common-query-runners/common-create-many-query-runner/common-create-many-query-runner.service.ts index 4e5d40757c..86d875427d 100644 --- a/packages/twenty-server/src/engine/api/common/common-query-runners/common-create-many-query-runner/common-create-many-query-runner.service.ts +++ b/packages/twenty-server/src/engine/api/common/common-query-runners/common-create-many-query-runner/common-create-many-query-runner.service.ts @@ -140,7 +140,7 @@ export class CommonCreateManyQueryRunnerService extends CommonBaseQueryRunnerSer return { ...args, - data: await this.dataArgProcessor.process({ + data: await this.queryRunnerArgsFactory.overrideDataByFieldMetadata({ partialRecordInputs: args.data, authContext, objectMetadataItemWithFieldMaps, diff --git a/packages/twenty-server/src/engine/api/common/common-query-runners/common-create-one-query-runner.service.ts b/packages/twenty-server/src/engine/api/common/common-query-runners/common-create-one-query-runner.service.ts index 635fad0b52..3fa4a53f9e 100644 --- a/packages/twenty-server/src/engine/api/common/common-query-runners/common-create-one-query-runner.service.ts +++ b/packages/twenty-server/src/engine/api/common/common-query-runners/common-create-one-query-runner.service.ts @@ -53,15 +53,15 @@ export class CommonCreateOneQueryRunnerService extends CommonBaseQueryRunnerServ ): Promise> { const { authContext, objectMetadataItemWithFieldMaps } = queryRunnerContext; - const coercedData = await this.dataArgProcessor.process({ - partialRecordInputs: [args.data], - authContext, - objectMetadataItemWithFieldMaps, - }); - return { ...args, - data: coercedData[0], + data: ( + await this.queryRunnerArgsFactory.overrideDataByFieldMetadata({ + partialRecordInputs: [args.data], + authContext, + objectMetadataItemWithFieldMaps, + }) + )[0], }; } diff --git a/packages/twenty-server/src/engine/api/common/common-query-runners/common-find-duplicates-query-runner.service.ts b/packages/twenty-server/src/engine/api/common/common-query-runners/common-find-duplicates-query-runner.service.ts index b16c488dc8..3d8553de21 100644 --- a/packages/twenty-server/src/engine/api/common/common-query-runners/common-find-duplicates-query-runner.service.ts +++ b/packages/twenty-server/src/engine/api/common/common-query-runners/common-find-duplicates-query-runner.service.ts @@ -171,7 +171,7 @@ export class CommonFindDuplicatesQueryRunnerService extends CommonBaseQueryRunne ), ) ?? [], ), - data: await this.dataArgProcessor.process({ + data: await this.queryRunnerArgsFactory.overrideDataByFieldMetadata({ partialRecordInputs: args.data, authContext, objectMetadataItemWithFieldMaps, diff --git a/packages/twenty-server/src/engine/api/common/common-query-runners/common-merge-many-query-runner.service.ts b/packages/twenty-server/src/engine/api/common/common-query-runners/common-merge-many-query-runner.service.ts index 9ddbd566f3..8caae1efef 100644 --- a/packages/twenty-server/src/engine/api/common/common-query-runners/common-merge-many-query-runner.service.ts +++ b/packages/twenty-server/src/engine/api/common/common-query-runners/common-merge-many-query-runner.service.ts @@ -29,6 +29,10 @@ import { CommonQueryNames, MergeManyQueryArgs, } from 'src/engine/api/common/types/common-query-args.type'; +import { + GraphqlQueryRunnerException, + GraphqlQueryRunnerExceptionCode, +} from 'src/engine/api/graphql/graphql-query-runner/errors/graphql-query-runner.exception'; import { buildColumnsToReturn } from 'src/engine/api/graphql/graphql-query-runner/utils/build-columns-to-return'; import { buildColumnsToSelect } from 'src/engine/api/graphql/graphql-query-runner/utils/build-columns-to-select'; import { hasRecordFieldValue } from 'src/engine/api/graphql/graphql-query-runner/utils/has-record-field-value.util'; @@ -170,9 +174,9 @@ export class CommonMergeManyQueryRunnerService extends CommonBaseQueryRunnerServ ); if (!priorityRecord) { - throw new CommonQueryRunnerException( + throw new GraphqlQueryRunnerException( 'Priority record not found', - CommonQueryRunnerExceptionCode.RECORD_NOT_FOUND, + GraphqlQueryRunnerExceptionCode.RECORD_NOT_FOUND, ); } diff --git a/packages/twenty-server/src/engine/api/common/common-query-runners/common-update-many-query-runner.service.ts b/packages/twenty-server/src/engine/api/common/common-query-runners/common-update-many-query-runner.service.ts index 60df7d4f67..28b3fd24a5 100644 --- a/packages/twenty-server/src/engine/api/common/common-query-runners/common-update-many-query-runner.service.ts +++ b/packages/twenty-server/src/engine/api/common/common-query-runners/common-update-many-query-runner.service.ts @@ -106,7 +106,7 @@ export class CommonUpdateManyQueryRunnerService extends CommonBaseQueryRunnerSer objectMetadataItemWithFieldMaps, ) || {}, data: ( - await this.dataArgProcessor.process({ + await this.queryRunnerArgsFactory.overrideDataByFieldMetadata({ partialRecordInputs: [args.data], authContext, objectMetadataItemWithFieldMaps, diff --git a/packages/twenty-server/src/engine/api/common/common-query-runners/common-update-one-query-runner.service.ts b/packages/twenty-server/src/engine/api/common/common-query-runners/common-update-one-query-runner.service.ts index c899bd641b..cfe1b79a6d 100644 --- a/packages/twenty-server/src/engine/api/common/common-query-runners/common-update-one-query-runner.service.ts +++ b/packages/twenty-server/src/engine/api/common/common-query-runners/common-update-one-query-runner.service.ts @@ -65,7 +65,7 @@ export class CommonUpdateOneQueryRunnerService extends CommonBaseQueryRunnerServ return { ...args, data: ( - await this.dataArgProcessor.process({ + await this.queryRunnerArgsFactory.overrideDataByFieldMetadata({ partialRecordInputs: [args.data], authContext, objectMetadataItemWithFieldMaps, diff --git a/packages/twenty-server/src/engine/api/common/common-query-runners/errors/common-query-runner.exception.ts b/packages/twenty-server/src/engine/api/common/common-query-runners/errors/common-query-runner.exception.ts index 41de004a6e..2e8335404a 100644 --- a/packages/twenty-server/src/engine/api/common/common-query-runners/errors/common-query-runner.exception.ts +++ b/packages/twenty-server/src/engine/api/common/common-query-runners/errors/common-query-runner.exception.ts @@ -7,7 +7,6 @@ export enum CommonQueryRunnerExceptionCode { INVALID_QUERY_INPUT = 'INVALID_QUERY_INPUT', INVALID_AUTH_CONTEXT = 'INVALID_AUTH_CONTEXT', ARGS_CONFLICT = 'ARGS_CONFLICT', - INVALID_ARGS_DATA = 'INVALID_ARGS_DATA', INVALID_ARGS_FIRST = 'INVALID_ARGS_FIRST', INVALID_ARGS_LAST = 'INVALID_ARGS_LAST', UPSERT_MULTIPLE_MATCHING_RECORDS_CONFLICT = 'UPSERT_MULTIPLE_MATCHING_RECORDS_CONFLICT', diff --git a/packages/twenty-server/src/engine/api/common/common-query-runners/utils/common-query-runner-to-graphql-api-exception-handler.util.ts b/packages/twenty-server/src/engine/api/common/common-query-runners/utils/common-query-runner-to-graphql-api-exception-handler.util.ts index 562ad7ccd9..426719a906 100644 --- a/packages/twenty-server/src/engine/api/common/common-query-runners/utils/common-query-runner-to-graphql-api-exception-handler.util.ts +++ b/packages/twenty-server/src/engine/api/common/common-query-runners/utils/common-query-runner-to-graphql-api-exception-handler.util.ts @@ -20,7 +20,6 @@ export const commonQueryRunnerToGraphqlApiExceptionHandler = ( case CommonQueryRunnerExceptionCode.INVALID_ARGS_FIRST: case CommonQueryRunnerExceptionCode.INVALID_ARGS_LAST: case CommonQueryRunnerExceptionCode.INVALID_QUERY_INPUT: - case CommonQueryRunnerExceptionCode.INVALID_ARGS_DATA: case CommonQueryRunnerExceptionCode.UPSERT_MULTIPLE_MATCHING_RECORDS_CONFLICT: case CommonQueryRunnerExceptionCode.INVALID_CURSOR: case CommonQueryRunnerExceptionCode.UPSERT_MAX_RECORDS_EXCEEDED: diff --git a/packages/twenty-server/src/engine/api/common/common-query-runners/utils/common-query-runner-to-rest-api-exception-handler.util.ts b/packages/twenty-server/src/engine/api/common/common-query-runners/utils/common-query-runner-to-rest-api-exception-handler.util.ts index 07a9c569f9..3c9e52ffd7 100644 --- a/packages/twenty-server/src/engine/api/common/common-query-runners/utils/common-query-runner-to-rest-api-exception-handler.util.ts +++ b/packages/twenty-server/src/engine/api/common/common-query-runners/utils/common-query-runner-to-rest-api-exception-handler.util.ts @@ -19,7 +19,6 @@ export const commonQueryRunnerToRestApiExceptionHandler = ( case CommonQueryRunnerExceptionCode.INVALID_ARGS_FIRST: case CommonQueryRunnerExceptionCode.INVALID_ARGS_LAST: case CommonQueryRunnerExceptionCode.INVALID_QUERY_INPUT: - case CommonQueryRunnerExceptionCode.INVALID_ARGS_DATA: case CommonQueryRunnerExceptionCode.UPSERT_MULTIPLE_MATCHING_RECORDS_CONFLICT: case CommonQueryRunnerExceptionCode.INVALID_CURSOR: case CommonQueryRunnerExceptionCode.UPSERT_MAX_RECORDS_EXCEEDED: diff --git a/packages/twenty-server/src/engine/api/common/core-common-api.module.ts b/packages/twenty-server/src/engine/api/common/core-common-api.module.ts index fa0916b327..be29c40913 100644 --- a/packages/twenty-server/src/engine/api/common/core-common-api.module.ts +++ b/packages/twenty-server/src/engine/api/common/core-common-api.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; -import { CommonArgsProcessors } from 'src/engine/api/common/common-args-processors/common-args-processors'; +import { CommonArgsHandlers } from 'src/engine/api/common/common-args-handlers/common-query-selected-fields/common-arg-handlers'; import { CommonQueryRunners } from 'src/engine/api/common/common-query-runners/common-query-runners'; import { CommonResultGettersService } from 'src/engine/api/common/common-result-getters/common-result-getters.service'; import { GroupByWithRecordsService } from 'src/engine/api/graphql/graphql-query-runner/group-by/services/group-by-with-records.service'; @@ -14,8 +14,6 @@ import { ApiKeyModule } from 'src/engine/core-modules/api-key/api-key.module'; import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module'; import { FileModule } from 'src/engine/core-modules/file/file.module'; import { MetricsModule } from 'src/engine/core-modules/metrics/metrics.module'; -import { RecordPositionModule } from 'src/engine/core-modules/record-position/record-position.module'; -import { RecordTransformerModule } from 'src/engine/core-modules/record-transformer/record-transformer.module'; import { ThrottlerModule } from 'src/engine/core-modules/throttler/throttler.module'; import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module'; import { RoleTargetsEntity } from 'src/engine/metadata-modules/role/role-targets.entity'; @@ -41,15 +39,13 @@ import { GlobalWorkspaceDataSourceModule } from 'src/engine/twenty-orm/global-wo ViewFilterGroupModule, ThrottlerModule, MetricsModule, - RecordPositionModule, - RecordTransformerModule, GlobalWorkspaceDataSourceModule, FeatureFlagModule, ], providers: [ ProcessNestedRelationsHelper, ProcessNestedRelationsV2Helper, - ...CommonArgsProcessors, + ...CommonArgsHandlers, ProcessAggregateHelper, ...CommonQueryRunners, CommonResultGettersService, diff --git a/packages/twenty-server/src/engine/api/common/types/common-selected-fields-result.type.ts b/packages/twenty-server/src/engine/api/common/types/common-selected-fields-result.type.ts index f30795d4f6..ac11854af9 100644 --- a/packages/twenty-server/src/engine/api/common/types/common-selected-fields-result.type.ts +++ b/packages/twenty-server/src/engine/api/common/types/common-selected-fields-result.type.ts @@ -7,5 +7,6 @@ export interface CommonSelectedFields { export type CommonSelectedFieldsResult = { select: CommonSelectedFields; relations: CommonSelectedFields; + //TODO = Refacto-common - to update when rest api will handle aggregates aggregate: Record; }; diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/__tests__/query-runner-args.factory.spec.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/__tests__/query-runner-args.factory.spec.ts new file mode 100644 index 0000000000..465e813765 --- /dev/null +++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/__tests__/query-runner-args.factory.spec.ts @@ -0,0 +1,224 @@ +import { Test, type TestingModule } from '@nestjs/testing'; + +import { FieldMetadataType } from 'twenty-shared/types'; + +import { type WorkspaceQueryRunnerOptions } from 'src/engine/api/graphql/workspace-query-runner/interfaces/query-runner-option.interface'; +import { ResolverArgsType } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface'; + +import { QueryRunnerArgsFactory } from 'src/engine/api/graphql/workspace-query-runner/factories/query-runner-args.factory'; +import { RecordPositionService } from 'src/engine/core-modules/record-position/services/record-position.service'; +import { RecordInputTransformerService } from 'src/engine/core-modules/record-transformer/services/record-input-transformer.service'; +import { type FieldMetadataMap } from 'src/engine/metadata-modules/types/field-metadata-map'; + +describe('QueryRunnerArgsFactory', () => { + const recordPositionService = { + overridePositionOnRecords: jest + .fn() + .mockImplementation( + ({ partialRecordInputs }: { partialRecordInputs: any[] }) => { + return Promise.resolve( + partialRecordInputs.map((record: any) => ({ + ...record, + position: + record.position === 'last' || !record.position + ? 2 + : record.position, + })), + ); + }, + ), + }; + const workspaceId = 'workspaceId'; + const options = { + authContext: { workspace: { id: workspaceId } }, + objectMetadataItemWithFieldMaps: { + isCustom: true, + nameSingular: 'testNumber', + fieldsById: { + 'position-id': { + type: FieldMetadataType.POSITION, + isCustom: true, + name: 'position', + }, + 'testNumber-id': { + type: FieldMetadataType.NUMBER, + isCustom: true, + name: 'testNumber', + }, + 'otherField-id': { + type: FieldMetadataType.TEXT, + isCustom: true, + name: 'otherField', + }, + } as unknown as FieldMetadataMap, + fieldIdByName: { + position: 'position-id', + testNumber: 'testNumber-id', + otherField: 'otherField-id', + }, + fieldIdByJoinColumnName: {}, + }, + } as unknown as WorkspaceQueryRunnerOptions; + + let factory: QueryRunnerArgsFactory; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + QueryRunnerArgsFactory, + RecordInputTransformerService, + { + provide: RecordPositionService, + useValue: recordPositionService, + }, + ], + }).compile(); + + factory = module.get(QueryRunnerArgsFactory); + }); + + it('should be defined', () => { + expect(factory).toBeDefined(); + }); + + describe('create', () => { + it('should simply return the args when data is an empty array', async () => { + const args = { + data: [], + }; + const result = await factory.create( + args, + options, + ResolverArgsType.CREATE_MANY, + ); + + expect(result).toEqual(args); + }); + + it('createMany type should override data position and number', async () => { + const args = { + id: 'uuid', + data: [{ position: 'last', testNumber: 1 }], + }; + + const result = await factory.create( + args, + options, + ResolverArgsType.CREATE_MANY, + ); + + const expectedArgs = { + partialRecordInputs: [{ position: 'last', testNumber: 1 }], + objectMetadata: { + isCustom: true, + nameSingular: 'testNumber', + fieldIdByName: { + position: 'position-id', + testNumber: 'testNumber-id', + otherField: 'otherField-id', + }, + }, + workspaceId, + shouldBackfillPositionIfUndefined: true, + }; + + expect( + recordPositionService.overridePositionOnRecords, + ).toHaveBeenCalledWith(expectedArgs); + expect(result).toEqual({ + id: 'uuid', + data: [{ position: 2, testNumber: 1 }], + }); + }); + + it('createMany type should override position if not present', async () => { + const args = { + id: 'uuid', + data: [{ testNumber: 1 }], + }; + + const result = await factory.create( + args, + options, + ResolverArgsType.CREATE_MANY, + ); + + const expectedArgs = { + partialRecordInputs: [{ testNumber: 1 }], + objectMetadata: { + isCustom: true, + nameSingular: 'testNumber', + fieldIdByName: { + position: 'position-id', + testNumber: 'testNumber-id', + otherField: 'otherField-id', + }, + }, + workspaceId, + shouldBackfillPositionIfUndefined: true, + }; + + expect( + recordPositionService.overridePositionOnRecords, + ).toHaveBeenCalledWith(expectedArgs); + expect(result).toEqual({ + id: 'uuid', + data: [{ position: 2, testNumber: 1 }], + }); + }); + + it('findMany type should override data position and number', async () => { + const args = { + id: 'uuid', + filter: { testNumber: { eq: 1 }, otherField: { eq: 'test' } }, + }; + + const result = await factory.create( + args, + options, + ResolverArgsType.FIND_MANY, + ); + + expect(result).toEqual({ + id: 'uuid', + filter: { testNumber: { eq: 1 }, otherField: { eq: 'test' } }, + }); + }); + + it('findOne type should override number in filter', async () => { + const args = { + id: 'uuid', + filter: { testNumber: { eq: 1 }, otherField: { eq: 'test' } }, + }; + + const result = await factory.create( + args, + options, + ResolverArgsType.FIND_ONE, + ); + + expect(result).toEqual({ + id: 'uuid', + filter: { testNumber: { eq: 1 }, otherField: { eq: 'test' } }, + }); + }); + + it('findDuplicates type should override number in data and id', async () => { + const args = { + ids: [123], + data: [{ testNumber: 1, otherField: 'test' }], + }; + + const result = await factory.create( + args, + options, + ResolverArgsType.FIND_DUPLICATES, + ); + + expect(result).toEqual({ + ids: [123], + data: [{ testNumber: 1, position: 2, otherField: 'test' }], + }); + }); + }); +}); diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/index.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/index.ts new file mode 100644 index 0000000000..8fe5dd226c --- /dev/null +++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/index.ts @@ -0,0 +1,8 @@ +import { QueryRunnerArgsFactory } from './query-runner-args.factory'; + +import { QueryResultGettersFactory } from './query-result-getters/query-result-getters.factory'; + +export const workspaceQueryRunnerFactories = [ + QueryRunnerArgsFactory, + QueryResultGettersFactory, +]; diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/query-result-getters.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/query-result-getters.factory.ts new file mode 100644 index 0000000000..bbd555ecb6 --- /dev/null +++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/query-result-getters.factory.ts @@ -0,0 +1,237 @@ +import { Injectable, Logger } from '@nestjs/common'; + +import { FieldMetadataType, type ObjectRecord } from 'twenty-shared/types'; +import { isDefined } from 'twenty-shared/utils'; + +import { type QueryResultFieldValue } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/interfaces/query-result-field-value'; +import { type QueryResultGetterHandlerInterface } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/interfaces/query-result-getter-handler.interface'; +import { type IConnection } from 'src/engine/api/graphql/workspace-query-runner/interfaces/connection.interface'; +import { type IEdge } from 'src/engine/api/graphql/workspace-query-runner/interfaces/edge.interface'; + +import { isQueryResultFieldValueAConnection } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/guards/is-query-result-field-value-a-connection.guard'; +import { isQueryResultFieldValueANestedRecordArray } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/guards/is-query-result-field-value-a-nested-record-array.guard'; +import { isQueryResultFieldValueARecordArray } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/guards/is-query-result-field-value-a-record-array.guard'; +import { isQueryResultFieldValueARecord } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/guards/is-query-result-field-value-a-record.guard'; +import { ActivityQueryResultGetterHandler } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/handlers/activity-query-result-getter.handler'; +import { AttachmentQueryResultGetterHandler } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/handlers/attachment-query-result-getter.handler'; +import { PersonQueryResultGetterHandler } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/handlers/person-query-result-getter.handler'; +import { WorkspaceMemberQueryResultGetterHandler } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/handlers/workspace-member-query-result-getter.handler'; +import { FileService } from 'src/engine/core-modules/file/services/file.service'; +import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps'; +import { type ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps'; +import { isFieldMetadataEntityOfType } from 'src/engine/utils/is-field-metadata-of-type.util'; + +// TODO: find a way to prevent conflict between handlers executing logic on object relations +// And this factory that is also executing logic on object relations +// Right now the factory will override any change made on relations by the handlers +@Injectable() +export class QueryResultGettersFactory { + private readonly logger = new Logger(QueryResultGettersFactory.name); + private handlers: Map; + + constructor(private readonly fileService: FileService) { + this.initializeHandlers(); + } + + private initializeHandlers() { + this.handlers = new Map([ + ['attachment', new AttachmentQueryResultGetterHandler(this.fileService)], + ['person', new PersonQueryResultGetterHandler(this.fileService)], + [ + 'workspaceMember', + new WorkspaceMemberQueryResultGetterHandler(this.fileService), + ], + ['note', new ActivityQueryResultGetterHandler(this.fileService)], + ['task', new ActivityQueryResultGetterHandler(this.fileService)], + ]); + } + + private async processConnection( + connection: IConnection, + objectMetadataItemId: string, + objectMetadataMaps: ObjectMetadataMaps, + workspaceId: string, + ): Promise> { + return { + ...connection, + edges: await Promise.all( + connection.edges.map(async (edge: IEdge) => ({ + ...edge, + node: await this.processRecord( + edge.node, + objectMetadataItemId, + objectMetadataMaps, + workspaceId, + ), + })), + ), + }; + } + + private async processNestedRecordArray( + result: { records: ObjectRecord[] }, + objectMetadataItemId: string, + objectMetadataMaps: ObjectMetadataMaps, + workspaceId: string, + ) { + return { + ...result, + records: await Promise.all( + result.records.map( + async (record: ObjectRecord) => + await this.processRecord( + record, + objectMetadataItemId, + objectMetadataMaps, + workspaceId, + ), + ), + ), + }; + } + + private async processRecordArray( + recordArray: ObjectRecord[], + objectMetadataItemId: string, + objectMetadataMaps: ObjectMetadataMaps, + workspaceId: string, + ) { + return await Promise.all( + recordArray.map( + async (record: ObjectRecord) => + await this.processRecord( + record, + objectMetadataItemId, + objectMetadataMaps, + workspaceId, + ), + ), + ); + } + + private async processRecord( + record: ObjectRecord, + objectMetadataItemId: string, + objectMetadataMaps: ObjectMetadataMaps, + workspaceId: string, + ): Promise { + const objectMetadataMapItem = objectMetadataMaps.byId[objectMetadataItemId]; + + if (!isDefined(objectMetadataMapItem)) { + throw new Error('Object metadata map item is not defined'); + } + + const handler = this.getHandler(objectMetadataMapItem.nameSingular); + + const relationFields = Object.keys(record) + .map( + (recordFieldName) => + objectMetadataMapItem.fieldsById[ + objectMetadataMapItem.fieldIdByName[recordFieldName] + ], + ) + .filter(isDefined) + .filter((fieldMetadata) => + isFieldMetadataEntityOfType(fieldMetadata, FieldMetadataType.RELATION), + ); + + const relationFieldsProcessedMap = {} as Record< + string, + QueryResultFieldValue + >; + + for (const relationField of relationFields) { + if (!isDefined(relationField.relationTargetObjectMetadataId)) { + throw new Error('Relation target object metadata id is not defined'); + } + + relationFieldsProcessedMap[relationField.name] = + await this.processQueryResultField( + record[relationField.name], + relationField.relationTargetObjectMetadataId, + objectMetadataMaps, + workspaceId, + ); + } + + const objectRecordProcessedWithoutRelationFields = await handler.handle( + record, + workspaceId, + ); + + const processedRecord = { + ...objectRecordProcessedWithoutRelationFields, + ...relationFieldsProcessedMap, + }; + + return processedRecord; + } + + private async processQueryResultField( + queryResultField: QueryResultFieldValue, + objectMetadataItemId: string, + objectMetadataMaps: ObjectMetadataMaps, + workspaceId: string, + ) { + if (isQueryResultFieldValueAConnection(queryResultField)) { + return await this.processConnection( + queryResultField, + objectMetadataItemId, + objectMetadataMaps, + workspaceId, + ); + } else if (isQueryResultFieldValueANestedRecordArray(queryResultField)) { + return await this.processNestedRecordArray( + queryResultField, + objectMetadataItemId, + objectMetadataMaps, + workspaceId, + ); + } else if (isQueryResultFieldValueARecordArray(queryResultField)) { + return await this.processRecordArray( + queryResultField, + objectMetadataItemId, + objectMetadataMaps, + workspaceId, + ); + } else if (isQueryResultFieldValueARecord(queryResultField)) { + return await this.processRecord( + queryResultField, + objectMetadataItemId, + objectMetadataMaps, + workspaceId, + ); + } else { + this.logger.warn( + `Query result field is not a record, connection, nested record array or record array. + This is an undetected case in query result getter that should be implemented !!`, + ); + + return queryResultField; + } + } + + async create( + result: QueryResultFieldValue, + objectMetadataItem: ObjectMetadataItemWithFieldMaps, + workspaceId: string, + objectMetadataMaps: ObjectMetadataMaps, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ): Promise { + return await this.processQueryResultField( + result, + objectMetadataItem.id, + objectMetadataMaps, + workspaceId, + ); + } + + private getHandler(objectType: string): QueryResultGetterHandlerInterface { + return ( + this.handlers.get(objectType) || { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + handle: (result: any) => result, + } + ); + } +} diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-runner-args.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-runner-args.factory.ts new file mode 100644 index 0000000000..303827eba8 --- /dev/null +++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-runner-args.factory.ts @@ -0,0 +1,323 @@ +import { Injectable } from '@nestjs/common'; + +import { FieldMetadataType, ObjectRecord } from 'twenty-shared/types'; +import { assertIsDefinedOrThrow, isDefined } from 'twenty-shared/utils'; + +import { type ObjectRecordFilter } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface'; +import { WorkspaceQueryRunnerOptions } from 'src/engine/api/graphql/workspace-query-runner/interfaces/query-runner-option.interface'; +import { + type CreateManyResolverArgs, + type CreateOneResolverArgs, + type FindDuplicatesResolverArgs, + type FindManyResolverArgs, + type FindOneResolverArgs, + GroupByResolverArgs, + type MergeManyResolverArgs, + type ResolverArgs, + ResolverArgsType, + type UpdateManyResolverArgs, + type UpdateOneResolverArgs, + type WorkspaceResolverBuilderMethodNames, +} from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface'; + +import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type'; +import { RecordPositionService } from 'src/engine/core-modules/record-position/services/record-position.service'; +import { RecordInputTransformerService } from 'src/engine/core-modules/record-transformer/services/record-input-transformer.service'; +import { WorkspaceNotFoundDefaultError } from 'src/engine/core-modules/workspace/workspace.exception'; +import { type FieldMetadataMap } from 'src/engine/metadata-modules/types/field-metadata-map'; +import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps'; + +@Injectable() +export class QueryRunnerArgsFactory { + constructor( + private readonly recordPositionService: RecordPositionService, + private readonly recordInputTransformerService: RecordInputTransformerService, + ) {} + + async create( + args: ResolverArgs, + options: WorkspaceQueryRunnerOptions, + resolverArgsType: WorkspaceResolverBuilderMethodNames, + ) { + const fieldMetadataMapByNameByName = + options.objectMetadataItemWithFieldMaps.fieldsById; + + const { objectMetadataItemWithFieldMaps, authContext } = options; + + switch (resolverArgsType) { + case ResolverArgsType.CREATE_ONE: + return { + ...args, + data: ( + await this.overrideDataByFieldMetadata({ + partialRecordInputs: [(args as CreateOneResolverArgs).data], + authContext, + objectMetadataItemWithFieldMaps, + }) + )[0], + } satisfies CreateOneResolverArgs; + case ResolverArgsType.CREATE_MANY: + return { + ...args, + data: await this.overrideDataByFieldMetadata({ + partialRecordInputs: (args as CreateManyResolverArgs).data, + authContext, + objectMetadataItemWithFieldMaps, + }), + } satisfies CreateManyResolverArgs; + case ResolverArgsType.UPDATE_ONE: + return { + ...args, + id: (args as UpdateOneResolverArgs).id, + data: ( + await this.overrideDataByFieldMetadata({ + partialRecordInputs: [(args as UpdateOneResolverArgs).data], + authContext, + objectMetadataItemWithFieldMaps, + shouldBackfillPositionIfUndefined: false, + }) + )[0], + } satisfies UpdateOneResolverArgs; + case ResolverArgsType.UPDATE_MANY: + return { + ...args, + filter: this.overrideFilterByFieldMetadata( + (args as UpdateManyResolverArgs).filter, + options.objectMetadataItemWithFieldMaps, + ), + data: ( + await this.overrideDataByFieldMetadata({ + partialRecordInputs: [(args as UpdateManyResolverArgs).data], + authContext, + objectMetadataItemWithFieldMaps, + shouldBackfillPositionIfUndefined: false, + }) + )[0], + } satisfies UpdateManyResolverArgs; + case ResolverArgsType.FIND_ONE: + return { + ...args, + filter: this.overrideFilterByFieldMetadata( + (args as FindOneResolverArgs).filter, + options.objectMetadataItemWithFieldMaps, + ), + }; + case ResolverArgsType.FIND_MANY: + return { + ...args, + filter: this.overrideFilterByFieldMetadata( + (args as FindManyResolverArgs).filter, + options.objectMetadataItemWithFieldMaps, + ), + }; + case ResolverArgsType.FIND_DUPLICATES: + return { + ...args, + ids: (await Promise.all( + (args as FindDuplicatesResolverArgs).ids?.map((id) => + this.overrideValueByFieldMetadata( + 'id', + id, + fieldMetadataMapByNameByName, + options.objectMetadataItemWithFieldMaps, + ), + ) ?? [], + )) as string[], + data: await this.overrideDataByFieldMetadata({ + partialRecordInputs: (args as FindDuplicatesResolverArgs).data, + authContext, + objectMetadataItemWithFieldMaps, + shouldBackfillPositionIfUndefined: false, + }), + } satisfies FindDuplicatesResolverArgs; + case ResolverArgsType.MERGE_MANY: + return { + ...args, + ids: (await Promise.all( + (args as MergeManyResolverArgs).ids?.map((id) => + this.overrideValueByFieldMetadata( + 'id', + id, + fieldMetadataMapByNameByName, + options.objectMetadataItemWithFieldMaps, + ), + ) ?? [], + )) as string[], + conflictPriorityIndex: (args as MergeManyResolverArgs) + .conflictPriorityIndex, + dryRun: (args as MergeManyResolverArgs).dryRun, + } satisfies MergeManyResolverArgs; + case ResolverArgsType.GROUP_BY: + return { + ...args, + filter: this.overrideFilterByFieldMetadata( + (args as GroupByResolverArgs).filter, + options.objectMetadataItemWithFieldMaps, + ), + }; + default: + return args; + } + } + + async overrideDataByFieldMetadata({ + partialRecordInputs, + authContext, + objectMetadataItemWithFieldMaps, + shouldBackfillPositionIfUndefined = true, + }: { + partialRecordInputs: Partial[] | undefined; + authContext: AuthContext; + objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps; + shouldBackfillPositionIfUndefined?: boolean; + }): Promise[]> { + if (!isDefined(partialRecordInputs)) { + return []; + } + + const allOverriddenRecords: Partial[] = []; + + const workspace = authContext.workspace; + + assertIsDefinedOrThrow(workspace, WorkspaceNotFoundDefaultError); + + const overriddenPositionRecords = + await this.recordPositionService.overridePositionOnRecords({ + partialRecordInputs, + workspaceId: workspace.id, + objectMetadata: { + isCustom: objectMetadataItemWithFieldMaps.isCustom, + nameSingular: objectMetadataItemWithFieldMaps.nameSingular, + fieldIdByName: objectMetadataItemWithFieldMaps.fieldIdByName, + }, + shouldBackfillPositionIfUndefined, + }); + + for (const record of overriddenPositionRecords) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const createArgByArgKey: [string, any][] = await Promise.all( + Object.entries(record).map(async ([key, value]) => { + const fieldMetadataId = + objectMetadataItemWithFieldMaps.fieldIdByName[key]; + const fieldMetadata = + objectMetadataItemWithFieldMaps.fieldsById[fieldMetadataId]; + + if (!fieldMetadata) { + return [key, value]; + } + + switch (fieldMetadata.type) { + case FieldMetadataType.NUMBER: + case FieldMetadataType.RICH_TEXT: + case FieldMetadataType.PHONES: + case FieldMetadataType.RICH_TEXT_V2: + case FieldMetadataType.LINKS: + case FieldMetadataType.EMAILS: { + const transformedRecord = + await this.recordInputTransformerService.process({ + recordInput: { [key]: value }, + objectMetadataMapItem: objectMetadataItemWithFieldMaps, + }); + + return [key, transformedRecord[key]]; + } + default: + return [key, value]; + } + }), + ); + + allOverriddenRecords.push(Object.fromEntries(createArgByArgKey)); + } + + return allOverriddenRecords; + } + + public overrideFilterByFieldMetadata< + T extends ObjectRecordFilter | undefined, + >( + filter: T, + objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps, + ): T { + if (!isDefined(filter)) { + return filter; + } + + const overrideFilter = (filterObject: ObjectRecordFilter) => { + return Object.entries(filterObject).reduce((acc, [key, value]) => { + if (key === 'and' || key === 'or') { + // @ts-expect-error legacy noImplicitAny + acc[key] = value.map((nestedFilter: ObjectRecordFilter) => + overrideFilter(nestedFilter), + ); + } else if (key === 'not') { + // @ts-expect-error legacy noImplicitAny + acc[key] = overrideFilter(value); + } else { + // @ts-expect-error legacy noImplicitAny + acc[key] = this.transformFilterValueByType( + key, + value, + objectMetadataItemWithFieldMaps, + ); + } + + return acc; + }, {}); + }; + + return overrideFilter(filter) as T; + } + + private transformFilterValueByType( + key: string, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + value: any, + objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps, + ) { + const fieldMetadataId = objectMetadataItemWithFieldMaps.fieldIdByName[key]; + const fieldMetadata = + objectMetadataItemWithFieldMaps.fieldsById[fieldMetadataId]; + + if (!fieldMetadata) { + return value; + } + + // Special handling for filter values, which have a specific structure + switch (fieldMetadata.type) { + case FieldMetadataType.NUMBER: { + if (value?.is === 'NULL') { + return value; + } else { + return Object.fromEntries( + Object.entries(value).map(([filterKey, filterValue]) => [ + filterKey, + Number(filterValue), + ]), + ); + } + } + default: + return value; + } + } + + async overrideValueByFieldMetadata( + key: string, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + value: any, + fieldMetadataMapByName: FieldMetadataMap, + objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps, + ) { + const fieldMetadata = fieldMetadataMapByName[key]; + + if (!fieldMetadata) { + return value; + } + + return this.recordInputTransformerService.process({ + recordInput: { [key]: value }, + objectMetadataMapItem: objectMetadataItemWithFieldMaps, + }); + } +} diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util.ts index e88d5ce7d4..013e8441c4 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util.ts @@ -22,6 +22,7 @@ import { twentyORMGraphqlApiExceptionHandler } from 'src/engine/twenty-orm/utils interface QueryFailedErrorWithCode extends QueryFailedError { code: string; } +//TODO : Refacto-common - Should be handle first in common api layer export const workspaceQueryRunnerGraphqlApiExceptionHandler = ( error: QueryFailedErrorWithCode, diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/workspace-query-runner.module.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/workspace-query-runner.module.ts index def86199a2..4cc34c2be0 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/workspace-query-runner.module.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/workspace-query-runner.module.ts @@ -2,6 +2,7 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { WorkspaceQueryBuilderModule } from 'src/engine/api/graphql/workspace-query-builder/workspace-query-builder.module'; +import { workspaceQueryRunnerFactories } from 'src/engine/api/graphql/workspace-query-runner/factories'; import { TelemetryListener } from 'src/engine/api/graphql/workspace-query-runner/listeners/telemetry.listener'; import { WorkspaceQueryHookModule } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/workspace-query-hook.module'; import { AuditModule } from 'src/engine/core-modules/audit/audit.module'; @@ -28,6 +29,11 @@ import { EntityEventsToDbListener } from './listeners/entity-events-to-db.listen RecordPositionModule, SubscriptionsModule, ], - providers: [EntityEventsToDbListener, TelemetryListener], + providers: [ + ...workspaceQueryRunnerFactories, + EntityEventsToDbListener, + TelemetryListener, + ], + exports: [...workspaceQueryRunnerFactories], }) export class WorkspaceQueryRunnerModule {} diff --git a/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-base.handler.ts b/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-base.handler.ts index e25031f2a7..55538a9f46 100644 --- a/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-base.handler.ts +++ b/packages/twenty-server/src/engine/api/rest/core/handlers/rest-api-base.handler.ts @@ -20,6 +20,7 @@ import { ApiKeyRoleService } from 'src/engine/core-modules/api-key/api-key-role. import { AccessTokenService } from 'src/engine/core-modules/auth/token/services/access-token.service'; import { WorkspaceDomainsService } from 'src/engine/core-modules/domain/workspace-domains/services/workspace-domains.service'; import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service'; +import { RecordInputTransformerService } from 'src/engine/core-modules/record-transformer/services/record-input-transformer.service'; import { WorkspaceNotFoundDefaultError } from 'src/engine/core-modules/workspace/workspace.exception'; import { PermissionsException, @@ -54,6 +55,8 @@ export interface FormatResult { } export abstract class RestApiBaseHandler { + @Inject() + protected readonly recordInputTransformerService: RecordInputTransformerService; @Inject() protected readonly twentyORMManager: TwentyORMManager; @Inject() diff --git a/packages/twenty-server/src/engine/api/rest/input-request-parsers/filter-parser-utils/parse-filter-rest-request.util.ts b/packages/twenty-server/src/engine/api/rest/input-request-parsers/filter-parser-utils/parse-filter-rest-request.util.ts index 0b8843e434..e75eb68afc 100644 --- a/packages/twenty-server/src/engine/api/rest/input-request-parsers/filter-parser-utils/parse-filter-rest-request.util.ts +++ b/packages/twenty-server/src/engine/api/rest/input-request-parsers/filter-parser-utils/parse-filter-rest-request.util.ts @@ -1,3 +1,4 @@ +//TODO : Refacto-common - remove this comment - This parser is a copy of the filter input factory without objectMetadata dependency. Validation will be done in common layer import { type FieldValue } from 'src/engine/api/rest/core/types/field-value.type'; import { addDefaultConjunctionIfMissing } from 'src/engine/api/rest/input-request-parsers/filter-parser-utils/add-default-conjunction.util'; import { checkFilterQuery } from 'src/engine/api/rest/input-request-parsers/filter-parser-utils/check-filter-query.util'; diff --git a/packages/twenty-server/src/engine/api/rest/input-request-parsers/order-by-parser-utils/parse-order-by-rest-request.util.ts b/packages/twenty-server/src/engine/api/rest/input-request-parsers/order-by-parser-utils/parse-order-by-rest-request.util.ts index d1a0520649..3340524102 100644 --- a/packages/twenty-server/src/engine/api/rest/input-request-parsers/order-by-parser-utils/parse-order-by-rest-request.util.ts +++ b/packages/twenty-server/src/engine/api/rest/input-request-parsers/order-by-parser-utils/parse-order-by-rest-request.util.ts @@ -1,6 +1,8 @@ +//TODO : Refacto-common - remove this comment - This parser is a copy of the OrderByInputFactory without objectMetadata dependency. Validation will be done in common layer + import { type ObjectRecordOrderBy } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface'; -import { parseOrderBy } from 'src/engine/api/rest/input-request-parsers/order-by-parser-utils/utils/parse-order-by-rest-request-common.util'; +import { parseOrderByRestRequestCommon } from 'src/engine/api/rest/input-request-parsers/order-by-parser-utils/utils/parse-order-by-rest-request-common.util'; import { type AuthenticatedRequest } from 'src/engine/api/rest/types/authenticated-request'; export const parseOrderByRestRequest = ( @@ -8,5 +10,5 @@ export const parseOrderByRestRequest = ( ): ObjectRecordOrderBy => { const orderByQuery = request.query.order_by; - return parseOrderBy(orderByQuery); + return parseOrderByRestRequestCommon(orderByQuery); }; diff --git a/packages/twenty-server/src/engine/api/rest/input-request-parsers/order-by-parser-utils/utils/parse-order-by-rest-request-common.util.ts b/packages/twenty-server/src/engine/api/rest/input-request-parsers/order-by-parser-utils/utils/parse-order-by-rest-request-common.util.ts index 6fd089b49a..f6030b7747 100644 --- a/packages/twenty-server/src/engine/api/rest/input-request-parsers/order-by-parser-utils/utils/parse-order-by-rest-request-common.util.ts +++ b/packages/twenty-server/src/engine/api/rest/input-request-parsers/order-by-parser-utils/utils/parse-order-by-rest-request-common.util.ts @@ -1,3 +1,5 @@ +//TODO : Refacto-common - remove this comment - This parser is a copy of the OrderByInputFactory without objectMetadata dependency. Validation will be done in common layer + import { OrderByDirection } from 'twenty-shared/types'; import { type ObjectRecordOrderBy } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface'; @@ -12,7 +14,7 @@ import { const DEFAULT_ORDER_DIRECTION = OrderByDirection.AscNullsFirst; -export const parseOrderBy = ( +export const parseOrderByRestRequestCommon = ( orderByQuery: string | string[] | ParsedQs | ParsedQs[] | undefined, ): ObjectRecordOrderBy => { if (typeof orderByQuery !== 'string') { diff --git a/packages/twenty-server/src/engine/api/rest/input-request-parsers/order-by-with-group-by-parser-utils/parse-order-by-for-records-rest-request.util.ts b/packages/twenty-server/src/engine/api/rest/input-request-parsers/order-by-with-group-by-parser-utils/parse-order-by-for-records-rest-request.util.ts index 69302bffa9..4047f9324c 100644 --- a/packages/twenty-server/src/engine/api/rest/input-request-parsers/order-by-with-group-by-parser-utils/parse-order-by-for-records-rest-request.util.ts +++ b/packages/twenty-server/src/engine/api/rest/input-request-parsers/order-by-with-group-by-parser-utils/parse-order-by-for-records-rest-request.util.ts @@ -1,6 +1,6 @@ import { type ObjectRecordOrderBy } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface'; -import { parseOrderBy } from 'src/engine/api/rest/input-request-parsers/order-by-parser-utils/utils/parse-order-by-rest-request-common.util'; +import { parseOrderByRestRequestCommon } from 'src/engine/api/rest/input-request-parsers/order-by-parser-utils/utils/parse-order-by-rest-request-common.util'; import { type AuthenticatedRequest } from 'src/engine/api/rest/types/authenticated-request'; export const parseOrderByForRecordsWithGroupByRestRequest = ( @@ -8,5 +8,5 @@ export const parseOrderByForRecordsWithGroupByRestRequest = ( ): ObjectRecordOrderBy | undefined => { const orderByForRecordsWithGroupByQuery = request.query.order_by_for_records; - return parseOrderBy(orderByForRecordsWithGroupByQuery); + return parseOrderByRestRequestCommon(orderByForRecordsWithGroupByQuery); }; diff --git a/packages/twenty-server/src/engine/core-modules/open-api/utils/__tests__/components.utils.spec.ts b/packages/twenty-server/src/engine/core-modules/open-api/utils/__tests__/components.utils.spec.ts index 125330843d..8f31d86e1f 100644 --- a/packages/twenty-server/src/engine/core-modules/open-api/utils/__tests__/components.utils.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/open-api/utils/__tests__/components.utils.spec.ts @@ -32,9 +32,9 @@ describe('computeSchemaComponents', () => { "lastName": "Osinski", }, "fieldLinks": { + "additionalLinks": [], "primaryLinkLabel": "", "primaryLinkUrl": "https://narrow-help.net/", - "secondaryLinks": [], }, "fieldMultiSelect": [ "OPTION_1", @@ -46,7 +46,9 @@ describe('computeSchemaComponents', () => { "primaryPhoneCountryCode": "FR", "primaryPhoneNumber": "06 10 20 30 40", }, - "fieldSelect": "OPTION_1", + "fieldSelect": [ + "OPTION_1", + ], }, "properties": { "fieldActor": { @@ -533,9 +535,9 @@ describe('computeSchemaComponents', () => { "lastName": "Jones", }, "fieldLinks": { + "additionalLinks": [], "primaryLinkLabel": "", "primaryLinkUrl": "https://unlawful-blowgun.biz", - "secondaryLinks": [], }, "fieldMultiSelect": [ "OPTION_1", @@ -547,7 +549,9 @@ describe('computeSchemaComponents', () => { "primaryPhoneCountryCode": "FR", "primaryPhoneNumber": "06 10 20 30 40", }, - "fieldSelect": "OPTION_1", + "fieldSelect": [ + "OPTION_1", + ], }, "properties": { "fieldActor": { diff --git a/packages/twenty-server/src/engine/core-modules/open-api/utils/generate-random-field-value.util.ts b/packages/twenty-server/src/engine/core-modules/open-api/utils/generate-random-field-value.util.ts index 9f28570216..25d0f9f73d 100644 --- a/packages/twenty-server/src/engine/core-modules/open-api/utils/generate-random-field-value.util.ts +++ b/packages/twenty-server/src/engine/core-modules/open-api/utils/generate-random-field-value.util.ts @@ -58,7 +58,7 @@ export const generateRandomFieldValue = ({ return { primaryLinkLabel: '', primaryLinkUrl: faker.internet.url(), - secondaryLinks: [], + additionalLinks: [], }; } @@ -82,10 +82,10 @@ export const generateRandomFieldValue = ({ case FieldMetadataType.SELECT: { if (!isDefined(field.options) || !isDefined(field.options[0].value)) { - return null; + return []; } - return field.options[0].value; + return [field.options[0].value]; } case FieldMetadataType.MULTI_SELECT: { diff --git a/packages/twenty-server/src/engine/core-modules/record-transformer/services/record-input-transformer.service.ts b/packages/twenty-server/src/engine/core-modules/record-transformer/services/record-input-transformer.service.ts index eb867ae12e..310b9274c4 100644 --- a/packages/twenty-server/src/engine/core-modules/record-transformer/services/record-input-transformer.service.ts +++ b/packages/twenty-server/src/engine/core-modules/record-transformer/services/record-input-transformer.service.ts @@ -2,16 +2,15 @@ import { Injectable } from '@nestjs/common'; import { FieldMetadataType, - ObjectRecord, compositeTypeDefinitions, + type RichTextV2Metadata, + richTextV2ValueSchema, } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; -import { transformEmailsValue } from 'src/engine/core-modules/record-transformer/utils/transform-emails-value.util'; import { transformLinksValue } from 'src/engine/core-modules/record-transformer/utils/transform-links-value.util'; import { transformPhonesValue } from 'src/engine/core-modules/record-transformer/utils/transform-phones-value.util'; -import { transformRichTextV2Value } from 'src/engine/core-modules/record-transformer/utils/transform-rich-text-v2.util'; -import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps'; +import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps'; @Injectable() export class RecordInputTransformerService { @@ -19,9 +18,15 @@ export class RecordInputTransformerService { recordInput, objectMetadataMapItem, }: { - recordInput: Partial; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + recordInput: Record; objectMetadataMapItem: ObjectMetadataItemWithFieldMaps; - }): Promise> { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + }): Promise> { + if (!recordInput) { + return recordInput; + } + let transformedEntries = {}; for (const [key, value] of Object.entries(recordInput)) { @@ -67,11 +72,11 @@ export class RecordInputTransformerService { 'Rich text is not supported, please use RICH_TEXT_V2 instead', ); case FieldMetadataType.RICH_TEXT_V2: - return await transformRichTextV2Value(value); + return this.transformRichTextV2Value(value); case FieldMetadataType.LINKS: return transformLinksValue(value); case FieldMetadataType.EMAILS: - return transformEmailsValue(value); + return this.transformEmailsValue(value); case FieldMetadataType.PHONES: return transformPhonesValue({ input: value }); default: @@ -79,6 +84,73 @@ export class RecordInputTransformerService { } } + private async transformRichTextV2Value( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + richTextValue: any, + ): Promise { + const parsedValue = richTextV2ValueSchema.parse(richTextValue); + + const { ServerBlockNoteEditor } = await import('@blocknote/server-util'); + + const serverBlockNoteEditor = ServerBlockNoteEditor.create(); + + // Patch: Handle cases where blocknote to markdown conversion fails for certain block types (custom/code blocks) + // Todo : This may be resolved once the server-utils library is updated with proper conversion support - #947 + let convertedMarkdown: string | null = null; + + try { + convertedMarkdown = isDefined(parsedValue.blocknote) + ? await serverBlockNoteEditor.blocksToMarkdownLossy( + JSON.parse(parsedValue.blocknote), + ) + : null; + } catch { + convertedMarkdown = parsedValue.blocknote || null; + } + + const convertedBlocknote = parsedValue.markdown + ? JSON.stringify( + await serverBlockNoteEditor.tryParseMarkdownToBlocks( + parsedValue.markdown, + ), + ) + : null; + + return { + markdown: parsedValue.markdown || convertedMarkdown, + blocknote: parsedValue.blocknote || convertedBlocknote, + }; + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + private transformEmailsValue(value: any): any { + if (!value) { + return value; + } + + let additionalEmails = value?.additionalEmails; + const primaryEmail = value?.primaryEmail + ? value.primaryEmail.toLowerCase() + : ''; + + if (additionalEmails) { + try { + const emailArray = JSON.parse(additionalEmails) as string[]; + + additionalEmails = JSON.stringify( + emailArray.map((email) => email.toLowerCase()), + ); + } catch { + /* empty */ + } + } + + return { + primaryEmail, + additionalEmails, + }; + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any private stringifySubFields(fieldMetadataType: FieldMetadataType, value: any) { const compositeType = compositeTypeDefinitions.get(fieldMetadataType); diff --git a/packages/twenty-server/src/engine/core-modules/record-transformer/utils/transform-emails-value.util.ts b/packages/twenty-server/src/engine/core-modules/record-transformer/utils/transform-emails-value.util.ts deleted file mode 100644 index 1c74bef302..0000000000 --- a/packages/twenty-server/src/engine/core-modules/record-transformer/utils/transform-emails-value.util.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { isNonEmptyString } from '@sniptt/guards'; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export const transformEmailsValue = (value: any): any => { - if (!value) { - return value; - } - - let additionalEmails = value?.additionalEmails; - const primaryEmail = value?.primaryEmail - ? value.primaryEmail.toLowerCase() - : ''; - - if (additionalEmails) { - try { - const emailArray = ( - isNonEmptyString(additionalEmails) - ? JSON.parse(additionalEmails) - : additionalEmails - ) as string[]; - - additionalEmails = JSON.stringify( - emailArray.map((email) => email.toLowerCase()), - ); - } catch { - /* empty */ - } - } - - return { - primaryEmail, - additionalEmails, - }; -}; diff --git a/packages/twenty-server/src/engine/core-modules/record-transformer/utils/transform-links-value.util.ts b/packages/twenty-server/src/engine/core-modules/record-transformer/utils/transform-links-value.util.ts index 1ddef8f80c..669cfd102c 100644 --- a/packages/twenty-server/src/engine/core-modules/record-transformer/utils/transform-links-value.util.ts +++ b/packages/twenty-server/src/engine/core-modules/record-transformer/utils/transform-links-value.util.ts @@ -32,7 +32,7 @@ export const transformLinksValue = ( const secondaryLinksArray = isNonEmptyString(secondaryLinksRaw) ? parseJson(secondaryLinksRaw) - : secondaryLinksRaw; + : null; const { primaryLinkLabel, primaryLinkUrl, secondaryLinks } = removeEmptyLinks( { diff --git a/packages/twenty-server/src/engine/core-modules/record-transformer/utils/transform-rich-text-v2.util.ts b/packages/twenty-server/src/engine/core-modules/record-transformer/utils/transform-rich-text-v2.util.ts deleted file mode 100644 index 44825e0bcc..0000000000 --- a/packages/twenty-server/src/engine/core-modules/record-transformer/utils/transform-rich-text-v2.util.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { isNonEmptyString } from '@sniptt/guards'; -import { - type RichTextV2Metadata, - richTextV2ValueSchema, -} from 'twenty-shared/types'; -import { isDefined } from 'twenty-shared/utils'; - -export const transformRichTextV2Value = async ( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - richTextValue: any, -): Promise => { - const parsedValue = isNonEmptyString(richTextValue) - ? richTextV2ValueSchema.parse(richTextValue) - : richTextValue; - - const { ServerBlockNoteEditor } = await import('@blocknote/server-util'); - - const serverBlockNoteEditor = ServerBlockNoteEditor.create(); - - // Patch: Handle cases where blocknote to markdown conversion fails for certain block types (custom/code blocks) - // Todo : This may be resolved once the server-utils library is updated with proper conversion support - #947 - let convertedMarkdown: string | null = null; - - try { - convertedMarkdown = isDefined(parsedValue.blocknote) - ? await serverBlockNoteEditor.blocksToMarkdownLossy( - JSON.parse(parsedValue.blocknote), - ) - : null; - } catch { - convertedMarkdown = parsedValue.blocknote || null; - } - - const convertedBlocknote = parsedValue.markdown - ? JSON.stringify( - await serverBlockNoteEditor.tryParseMarkdownToBlocks( - parsedValue.markdown, - ), - ) - : null; - - return { - markdown: parsedValue.markdown || convertedMarkdown, - blocknote: parsedValue.blocknote || convertedBlocknote, - }; -}; diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/address-field-create-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/address-field-create-input-validation.integration-spec.ts.snap deleted file mode 100644 index c3d6399b05..0000000000 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/address-field-create-input-validation.integration-spec.ts.snap +++ /dev/null @@ -1,5 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Create input validation - ADDRESS Gql create input - failure ADDRESS - should fail with : {"addressField":"not-an-address"} 1`] = `"Expected type "AddressCreateInput" to be an object."`; - -exports[`Create input validation - ADDRESS Rest create input - failure ADDRESS - should fail with : {"addressField":"not-an-address"} 1`] = `"["Invalid object value 'not-an-address' for field \\"addressField\\""]"`; diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/array-field-create-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/array-field-create-input-validation.integration-spec.ts.snap index b989262df5..379dc37988 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/array-field-create-input-validation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/array-field-create-input-validation.integration-spec.ts.snap @@ -4,6 +4,6 @@ exports[`Create input validation - ARRAY Gql create input - failure ARRAY - shou exports[`Create input validation - ARRAY Gql create input - failure ARRAY - should fail with : {"arrayField":true} 1`] = `"String cannot represent a non string value: true"`; -exports[`Create input validation - ARRAY Rest create input - failure ARRAY - should fail with : {"arrayField":1} 1`] = `"["Invalid value 1 for field \\"arrayField - Array values need to be string\\""]"`; +exports[`Create input validation - ARRAY Rest create input - failure ARRAY - should fail with : {"arrayField":1} 1`] = `"["malformed array literal: \\"1\\""]"`; -exports[`Create input validation - ARRAY Rest create input - failure ARRAY - should fail with : {"arrayField":true} 1`] = `"["Invalid value true for field \\"arrayField - Array values need to be string\\""]"`; +exports[`Create input validation - ARRAY Rest create input - failure ARRAY - should fail with : {"arrayField":true} 1`] = `"["malformed array literal: \\"true\\""]"`; diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/currency-field-create-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/currency-field-create-input-validation.integration-spec.ts.snap deleted file mode 100644 index 2285c3ae82..0000000000 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/currency-field-create-input-validation.integration-spec.ts.snap +++ /dev/null @@ -1,5 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Create input validation - CURRENCY Gql create input - failure CURRENCY - should fail with : {"currencyField":"not-a-currency"} 1`] = `"Expected type "CurrencyCreateInput" to be an object."`; - -exports[`Create input validation - CURRENCY Rest create input - failure CURRENCY - should fail with : {"currencyField":"not-a-currency"} 1`] = `"["Invalid object value 'not-a-currency' for field \\"currencyField\\""]"`; diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/date-field-create-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/date-field-create-input-validation.integration-spec.ts.snap index 855656c35c..5f86a83313 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/date-field-create-input-validation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/date-field-create-input-validation.integration-spec.ts.snap @@ -1,21 +1,21 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Create input validation - DATE Gql create input - failure DATE - should fail with : {"dateField":"malformed-date"} 1`] = `"Invalid value 'malformed-date' for date or date-time field "dateField""`; +exports[`Create input validation - DATE Gql create input - failure DATE - should fail with : {"dateField":"malformed-date"} 1`] = `"invalid input syntax for type date: "malformed-date""`; -exports[`Create input validation - DATE Gql create input - failure DATE - should fail with : {"dateField":[]} 1`] = `"Invalid value [] for date or date-time field "dateField""`; +exports[`Create input validation - DATE Gql create input - failure DATE - should fail with : {"dateField":[]} 1`] = `"invalid input syntax for type date: "{}""`; -exports[`Create input validation - DATE Gql create input - failure DATE - should fail with : {"dateField":{}} 1`] = `"Invalid value {} for date or date-time field "dateField""`; +exports[`Create input validation - DATE Gql create input - failure DATE - should fail with : {"dateField":{}} 1`] = `"invalid input syntax for type date: "{}""`; exports[`Create input validation - DATE Gql create input - failure DATE - should fail with : {"dateField":1} 1`] = `"invalid input syntax for type date: "1""`; -exports[`Create input validation - DATE Gql create input - failure DATE - should fail with : {"dateField":true} 1`] = `"Invalid value true for date or date-time field "dateField""`; +exports[`Create input validation - DATE Gql create input - failure DATE - should fail with : {"dateField":true} 1`] = `"invalid input syntax for type date: "true""`; -exports[`Create input validation - DATE Rest create input - failure DATE - should fail with : {"dateField":"malformed-date"} 1`] = `"["Invalid value 'malformed-date' for date or date-time field \\"dateField\\""]"`; +exports[`Create input validation - DATE Rest create input - failure DATE - should fail with : {"dateField":"malformed-date"} 1`] = `"["invalid input syntax for type date: \\"malformed-date\\""]"`; -exports[`Create input validation - DATE Rest create input - failure DATE - should fail with : {"dateField":[]} 1`] = `"["Invalid value [] for date or date-time field \\"dateField\\""]"`; +exports[`Create input validation - DATE Rest create input - failure DATE - should fail with : {"dateField":[]} 1`] = `"["invalid input syntax for type date: \\"{}\\""]"`; -exports[`Create input validation - DATE Rest create input - failure DATE - should fail with : {"dateField":{}} 1`] = `"["Invalid value {} for date or date-time field \\"dateField\\""]"`; +exports[`Create input validation - DATE Rest create input - failure DATE - should fail with : {"dateField":{}} 1`] = `"["invalid input syntax for type date: \\"{}\\""]"`; exports[`Create input validation - DATE Rest create input - failure DATE - should fail with : {"dateField":1} 1`] = `"["invalid input syntax for type date: \\"1\\""]"`; -exports[`Create input validation - DATE Rest create input - failure DATE - should fail with : {"dateField":true} 1`] = `"["Invalid value true for date or date-time field \\"dateField\\""]"`; +exports[`Create input validation - DATE Rest create input - failure DATE - should fail with : {"dateField":true} 1`] = `"["invalid input syntax for type date: \\"true\\""]"`; diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/date-time-field-create-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/date-time-field-create-input-validation.integration-spec.ts.snap index ef4e139e45..413ee14d76 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/date-time-field-create-input-validation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/date-time-field-create-input-validation.integration-spec.ts.snap @@ -1,21 +1,21 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Create input validation - DATE_TIME Gql create input - failure DATE_TIME - should fail with : {"dateTimeField":"malformed-date"} 1`] = `"Invalid value 'malformed-date' for date or date-time field "dateTimeField""`; +exports[`Create input validation - DATE_TIME Gql create input - failure DATE_TIME - should fail with : {"dateTimeField":"malformed-date"} 1`] = `"invalid input syntax for type timestamp with time zone: "malformed-date""`; -exports[`Create input validation - DATE_TIME Gql create input - failure DATE_TIME - should fail with : {"dateTimeField":[]} 1`] = `"Invalid value [] for date or date-time field "dateTimeField""`; +exports[`Create input validation - DATE_TIME Gql create input - failure DATE_TIME - should fail with : {"dateTimeField":[]} 1`] = `"invalid input syntax for type timestamp with time zone: "{}""`; -exports[`Create input validation - DATE_TIME Gql create input - failure DATE_TIME - should fail with : {"dateTimeField":{}} 1`] = `"Invalid value {} for date or date-time field "dateTimeField""`; +exports[`Create input validation - DATE_TIME Gql create input - failure DATE_TIME - should fail with : {"dateTimeField":{}} 1`] = `"invalid input syntax for type timestamp with time zone: "{}""`; exports[`Create input validation - DATE_TIME Gql create input - failure DATE_TIME - should fail with : {"dateTimeField":1} 1`] = `"invalid input syntax for type timestamp with time zone: "1""`; -exports[`Create input validation - DATE_TIME Gql create input - failure DATE_TIME - should fail with : {"dateTimeField":true} 1`] = `"Invalid value true for date or date-time field "dateTimeField""`; +exports[`Create input validation - DATE_TIME Gql create input - failure DATE_TIME - should fail with : {"dateTimeField":true} 1`] = `"invalid input syntax for type timestamp with time zone: "true""`; -exports[`Create input validation - DATE_TIME Rest create input - failure DATE_TIME - should fail with : {"dateTimeField":"malformed-date"} 1`] = `"["Invalid value 'malformed-date' for date or date-time field \\"dateTimeField\\""]"`; +exports[`Create input validation - DATE_TIME Rest create input - failure DATE_TIME - should fail with : {"dateTimeField":"malformed-date"} 1`] = `"["invalid input syntax for type timestamp with time zone: \\"malformed-date\\""]"`; -exports[`Create input validation - DATE_TIME Rest create input - failure DATE_TIME - should fail with : {"dateTimeField":[]} 1`] = `"["Invalid value [] for date or date-time field \\"dateTimeField\\""]"`; +exports[`Create input validation - DATE_TIME Rest create input - failure DATE_TIME - should fail with : {"dateTimeField":[]} 1`] = `"["invalid input syntax for type timestamp with time zone: \\"{}\\""]"`; -exports[`Create input validation - DATE_TIME Rest create input - failure DATE_TIME - should fail with : {"dateTimeField":{}} 1`] = `"["Invalid value {} for date or date-time field \\"dateTimeField\\""]"`; +exports[`Create input validation - DATE_TIME Rest create input - failure DATE_TIME - should fail with : {"dateTimeField":{}} 1`] = `"["invalid input syntax for type timestamp with time zone: \\"{}\\""]"`; exports[`Create input validation - DATE_TIME Rest create input - failure DATE_TIME - should fail with : {"dateTimeField":1} 1`] = `"["invalid input syntax for type timestamp with time zone: \\"1\\""]"`; -exports[`Create input validation - DATE_TIME Rest create input - failure DATE_TIME - should fail with : {"dateTimeField":true} 1`] = `"["Invalid value true for date or date-time field \\"dateTimeField\\""]"`; +exports[`Create input validation - DATE_TIME Rest create input - failure DATE_TIME - should fail with : {"dateTimeField":true} 1`] = `"["invalid input syntax for type timestamp with time zone: \\"true\\""]"`; diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/emails-field-create-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/emails-field-create-input-validation.integration-spec.ts.snap deleted file mode 100644 index 9dc03eff06..0000000000 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/emails-field-create-input-validation.integration-spec.ts.snap +++ /dev/null @@ -1,5 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Create input validation - EMAILS Gql create input - failure EMAILS - should fail with : {"emailsField":"not-an-email"} 1`] = `"Expected type "EmailsCreateInput" to be an object."`; - -exports[`Create input validation - EMAILS Rest create input - failure EMAILS - should fail with : {"emailsField":"not-an-email"} 1`] = `"["Invalid object value 'not-an-email' for field \\"emailsField\\""]"`; diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/full-name-field-create-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/full-name-field-create-input-validation.integration-spec.ts.snap deleted file mode 100644 index b99eafee13..0000000000 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/full-name-field-create-input-validation.integration-spec.ts.snap +++ /dev/null @@ -1,5 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Create input validation - FULL_NAME Gql create input - failure FULL_NAME - should fail with : {"fullNameField":"not-a-full-name"} 1`] = `"Expected type "FullNameCreateInput" to be an object."`; - -exports[`Create input validation - FULL_NAME Rest create input - failure FULL_NAME - should fail with : {"fullNameField":"not-a-full-name"} 1`] = `"["Invalid object value 'not-a-full-name' for field \\"fullNameField\\""]"`; diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/links-field-create-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/links-field-create-input-validation.integration-spec.ts.snap deleted file mode 100644 index 21fb5ebe68..0000000000 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/links-field-create-input-validation.integration-spec.ts.snap +++ /dev/null @@ -1,5 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Create input validation - LINKS Gql create input - failure LINKS - should fail with : {"linksField":"not-a-link"} 1`] = `"Expected type "LinksCreateInput" to be an object."`; - -exports[`Create input validation - LINKS Rest create input - failure LINKS - should fail with : {"linksField":"not-a-link"} 1`] = `"["Invalid object value 'not-a-link' for field \\"linksField\\""]"`; diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/multi-select-field-create-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/multi-select-field-create-input-validation.integration-spec.ts.snap index d58631baa1..538d8a7512 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/multi-select-field-create-input-validation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/multi-select-field-create-input-validation.integration-spec.ts.snap @@ -2,16 +2,12 @@ exports[`Create input validation - MULTI_SELECT Gql create input - failure MULTI_SELECT - should fail with : {"multiSelectField":"not-a-select-option"} 1`] = `"Value "not-a-select-option" does not exist in "ApiInputValidationTestObjectMultiSelectFieldEnum" enum."`; -exports[`Create input validation - MULTI_SELECT Gql create input - failure MULTI_SELECT - should fail with : {"multiSelectField":{}} 1`] = `"Enum "ApiInputValidationTestObjectMultiSelectFieldEnum" cannot represent non-string value: {}."`; - exports[`Create input validation - MULTI_SELECT Gql create input - failure MULTI_SELECT - should fail with : {"multiSelectField":1} 1`] = `"Enum "ApiInputValidationTestObjectMultiSelectFieldEnum" cannot represent non-string value: 1."`; exports[`Create input validation - MULTI_SELECT Gql create input - failure MULTI_SELECT - should fail with : {"multiSelectField":true} 1`] = `"Enum "ApiInputValidationTestObjectMultiSelectFieldEnum" cannot represent non-string value: true."`; -exports[`Create input validation - MULTI_SELECT Rest create input - failure MULTI_SELECT - should fail with : {"multiSelectField":"not-a-select-option"} 1`] = `"["Invalid value 'not-a-select-option' for multi select field \\"multiSelectField\\""]"`; +exports[`Create input validation - MULTI_SELECT Rest create input - failure MULTI_SELECT - should fail with : {"multiSelectField":"not-a-select-option"} 1`] = `"["malformed array literal: \\"not-a-select-option\\""]"`; -exports[`Create input validation - MULTI_SELECT Rest create input - failure MULTI_SELECT - should fail with : {"multiSelectField":{}} 1`] = `"["Invalid value {} for field \\"multiSelectField - Array values need to be string\\""]"`; +exports[`Create input validation - MULTI_SELECT Rest create input - failure MULTI_SELECT - should fail with : {"multiSelectField":1} 1`] = `"["malformed array literal: \\"1\\""]"`; -exports[`Create input validation - MULTI_SELECT Rest create input - failure MULTI_SELECT - should fail with : {"multiSelectField":1} 1`] = `"["Invalid value 1 for field \\"multiSelectField - Array values need to be string\\""]"`; - -exports[`Create input validation - MULTI_SELECT Rest create input - failure MULTI_SELECT - should fail with : {"multiSelectField":true} 1`] = `"["Invalid value true for field \\"multiSelectField - Array values need to be string\\""]"`; +exports[`Create input validation - MULTI_SELECT Rest create input - failure MULTI_SELECT - should fail with : {"multiSelectField":true} 1`] = `"["malformed array literal: \\"true\\""]"`; diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/phones-field-create-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/phones-field-create-input-validation.integration-spec.ts.snap deleted file mode 100644 index 542a8d2973..0000000000 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/phones-field-create-input-validation.integration-spec.ts.snap +++ /dev/null @@ -1,5 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Create input validation - PHONES Gql create input - failure PHONES - should fail with : {"phonesField":"not-a-phone"} 1`] = `"Expected type "PhonesCreateInput" to be an object."`; - -exports[`Create input validation - PHONES Rest create input - failure PHONES - should fail with : {"phonesField":"not-a-phone"} 1`] = `"["Invalid object value 'not-a-phone' for field \\"phonesField\\""]"`; diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/rating-field-create-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/rating-field-create-input-validation.integration-spec.ts.snap index a676ced67d..95420d0bbb 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/rating-field-create-input-validation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/rating-field-create-input-validation.integration-spec.ts.snap @@ -10,12 +10,12 @@ exports[`Create input validation - RATING Gql create input - failure RATING - sh exports[`Create input validation - RATING Gql create input - failure RATING - should fail with : {"ratingField":true} 1`] = `"Enum "ApiInputValidationTestObjectRatingFieldEnum" cannot represent non-string value: true."`; -exports[`Create input validation - RATING Rest create input - failure RATING - should fail with : {"ratingField":"not-a-rating"} 1`] = `"["Invalid value 'not-a-rating' for field \\"ratingField\\""]"`; +exports[`Create input validation - RATING Rest create input - failure RATING - should fail with : {"ratingField":"not-a-rating"} 1`] = `"["invalid input value for enum workspace_1wgvd1injqtife6y4rvfbu3h5.\\"_apiInputValidationTestObject_ratingField_enum\\": \\"not-a-rating\\""]"`; -exports[`Create input validation - RATING Rest create input - failure RATING - should fail with : {"ratingField":[]} 1`] = `"["Invalid string value [] for text field \\"ratingField\\""]"`; +exports[`Create input validation - RATING Rest create input - failure RATING - should fail with : {"ratingField":[]} 1`] = `"["invalid input value for enum workspace_1wgvd1injqtife6y4rvfbu3h5.\\"_apiInputValidationTestObject_ratingField_enum\\": \\"\\""]"`; -exports[`Create input validation - RATING Rest create input - failure RATING - should fail with : {"ratingField":{}} 1`] = `"["Invalid string value {} for text field \\"ratingField\\""]"`; +exports[`Create input validation - RATING Rest create input - failure RATING - should fail with : {"ratingField":{}} 1`] = `"["invalid input value for enum workspace_1wgvd1injqtife6y4rvfbu3h5.\\"_apiInputValidationTestObject_ratingField_enum\\": \\"[object Object]\\""]"`; -exports[`Create input validation - RATING Rest create input - failure RATING - should fail with : {"ratingField":1} 1`] = `"["Invalid string value 1 for text field \\"ratingField\\""]"`; +exports[`Create input validation - RATING Rest create input - failure RATING - should fail with : {"ratingField":1} 1`] = `"["invalid input value for enum workspace_1wgvd1injqtife6y4rvfbu3h5.\\"_apiInputValidationTestObject_ratingField_enum\\": \\"1\\""]"`; -exports[`Create input validation - RATING Rest create input - failure RATING - should fail with : {"ratingField":true} 1`] = `"["Invalid string value true for text field \\"ratingField\\""]"`; +exports[`Create input validation - RATING Rest create input - failure RATING - should fail with : {"ratingField":true} 1`] = `"["invalid input value for enum workspace_1wgvd1injqtife6y4rvfbu3h5.\\"_apiInputValidationTestObject_ratingField_enum\\": \\"true\\""]"`; diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/raw-json-field-create-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/raw-json-field-create-input-validation.integration-spec.ts.snap index 925ad17fcc..ce3e029b04 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/raw-json-field-create-input-validation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/raw-json-field-create-input-validation.integration-spec.ts.snap @@ -1,5 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Create input validation - RAW_JSON Gql create input - failure RAW_JSON - should fail with : {"rawJsonField":"not-a-json"} 1`] = `"Invalid object value 'not-a-json' for field "rawJsonField""`; +exports[`Create input validation - RAW_JSON Gql create input - failure RAW_JSON - should fail with : {"rawJsonField":"not-a-json"} 1`] = `"Unexpected token 'o', "not-a-json" is not valid JSON"`; -exports[`Create input validation - RAW_JSON Rest create input - failure RAW_JSON - should fail with : {"rawJsonField":"not-a-json"} 1`] = `"["Invalid object value 'not-a-json' for field \\"rawJsonField\\""]"`; +exports[`Create input validation - RAW_JSON Rest create input - failure RAW_JSON - should fail with : {"rawJsonField":"not-a-json"} 1`] = `"["Unexpected token 'o', \\"not-a-json\\" is not valid JSON"]"`; \ No newline at end of file diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/relation-field-create-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/relation-field-create-input-validation.integration-spec.ts.snap index bf9cdb77bc..78aab899a4 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/relation-field-create-input-validation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/relation-field-create-input-validation.integration-spec.ts.snap @@ -1,29 +1,17 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Create input validation - RELATION Gql create input - failure RELATION - should fail with : {"manyToOneRelationFieldId":"non-uuid"} 1`] = `"Invalid UUID value 'non-uuid' for field "manyToOneRelationFieldId""`; +exports[`Create input validation - RELATION Gql create input - failure RELATION - should fail with : {"manyToOneRelationFieldId":"non-uuid"} 1`] = `"invalid input syntax for type uuid: "non-uuid""`; -exports[`Create input validation - RELATION Gql create input - failure RELATION - should fail with : {"manyToOneRelationFieldId":[]} 1`] = `"ID cannot represent value: []"`; - -exports[`Create input validation - RELATION Gql create input - failure RELATION - should fail with : {"manyToOneRelationFieldId":{}} 1`] = `"ID cannot represent value: {}"`; - -exports[`Create input validation - RELATION Gql create input - failure RELATION - should fail with : {"manyToOneRelationFieldId":1} 1`] = `"Invalid UUID value '1' for field "manyToOneRelationFieldId""`; +exports[`Create input validation - RELATION Gql create input - failure RELATION - should fail with : {"manyToOneRelationFieldId":1} 1`] = `"invalid input syntax for type uuid: "1""`; exports[`Create input validation - RELATION Gql create input - failure RELATION - should fail with : {"manyToOneRelationFieldId":true} 1`] = `"ID cannot represent value: true"`; exports[`Create input validation - RELATION Gql create input - failure RELATION - should fail with : {"oneToManyRelationFieldId":"not-existing-field"} 1`] = `"Field "oneToManyRelationFieldId" is not defined by type "ApiInputValidationTestObjectCreateInput". Did you mean "manyToOneRelationFieldId" or "manyToOneRelationField"?"`; -exports[`Create input validation - RELATION Gql create input - failure RELATION - should fail with : {"oneToOneRelationField":"not-existing-field"} 1`] = `"Field "oneToOneRelationField" is not defined by type "ApiInputValidationTestObjectCreateInput". Did you mean "manyToOneRelationField" or "manyToOneRelationFieldId"?"`; +exports[`Create input validation - RELATION Rest create input - failure RELATION - should fail with : {"manyToOneRelationFieldId":"non-uuid"} 1`] = `"["invalid input syntax for type uuid: \\"non-uuid\\""]"`; -exports[`Create input validation - RELATION Rest create input - failure RELATION - should fail with : {"manyToOneRelationFieldId":"non-uuid"} 1`] = `"["Invalid UUID value 'non-uuid' for field \\"manyToOneRelationFieldId\\""]"`; +exports[`Create input validation - RELATION Rest create input - failure RELATION - should fail with : {"manyToOneRelationFieldId":1} 1`] = `"["invalid input syntax for type uuid: \\"1\\""]"`; -exports[`Create input validation - RELATION Rest create input - failure RELATION - should fail with : {"manyToOneRelationFieldId":[]} 1`] = `"["Invalid UUID value [] for field \\"manyToOneRelationFieldId\\""]"`; +exports[`Create input validation - RELATION Rest create input - failure RELATION - should fail with : {"manyToOneRelationFieldId":true} 1`] = `"["invalid input syntax for type uuid: \\"true\\""]"`; -exports[`Create input validation - RELATION Rest create input - failure RELATION - should fail with : {"manyToOneRelationFieldId":{}} 1`] = `"["Invalid UUID value {} for field \\"manyToOneRelationFieldId\\""]"`; - -exports[`Create input validation - RELATION Rest create input - failure RELATION - should fail with : {"manyToOneRelationFieldId":1} 1`] = `"["Invalid UUID value 1 for field \\"manyToOneRelationFieldId\\""]"`; - -exports[`Create input validation - RELATION Rest create input - failure RELATION - should fail with : {"manyToOneRelationFieldId":true} 1`] = `"["Invalid UUID value true for field \\"manyToOneRelationFieldId\\""]"`; - -exports[`Create input validation - RELATION Rest create input - failure RELATION - should fail with : {"oneToManyRelationFieldId":"not-existing-field"} 1`] = `"["Object apiInputValidationTestObject doesn't have any \\"oneToManyRelationFieldId\\" field."]"`; - -exports[`Create input validation - RELATION Rest create input - failure RELATION - should fail with : {"oneToOneRelationField":"not-existing-field"} 1`] = `"["Object apiInputValidationTestObject doesn't have any \\"oneToOneRelationField\\" field."]"`; +exports[`Create input validation - RELATION Rest create input - failure RELATION - should fail with : {"oneToManyRelationFieldId":"not-existing-field"} 1`] = `"["Field metadata for field \\"oneToManyRelationFieldId\\" is missing in object metadata apiInputValidationTestObject"]"`; diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/rich-text-field-create-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/rich-text-field-create-input-validation.integration-spec.ts.snap index 3765c3e245..52b700a008 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/rich-text-field-create-input-validation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/rich-text-field-create-input-validation.integration-spec.ts.snap @@ -1,5 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Create input validation - RICH_TEXT Gql create input - failure RICH_TEXT - should fail with : {"richTextField":"test"} 1`] = `"richTextField RICH_TEXT-typed field does not support write operations"`; +exports[`Create input validation - RICH_TEXT Gql create input - failure RICH_TEXT - should fail with : {"richTextField":"test"} 1`] = `"Rich text is not supported, please use RICH_TEXT_V2 instead"`; -exports[`Create input validation - RICH_TEXT Rest create input - failure RICH_TEXT - should fail with : {"richTextField":"test"} 1`] = `"["richTextField RICH_TEXT-typed field does not support write operations"]"`; +exports[`Create input validation - RICH_TEXT Rest create input - failure RICH_TEXT - should fail with : {"richTextField":"test"} 1`] = `"["Rich text is not supported, please use RICH_TEXT_V2 instead"]"`; diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/rich-text-v2-field-create-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/rich-text-v2-field-create-input-validation.integration-spec.ts.snap deleted file mode 100644 index da48013133..0000000000 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/rich-text-v2-field-create-input-validation.integration-spec.ts.snap +++ /dev/null @@ -1,5 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Create input validation - RICH_TEXT_V2 Gql create input - failure RICH_TEXT_V2 - should fail with : {"richTextV2Field":"not-a-rich-text"} 1`] = `"Expected type "RichTextV2CreateInput" to be an object."`; - -exports[`Create input validation - RICH_TEXT_V2 Rest create input - failure RICH_TEXT_V2 - should fail with : {"richTextV2Field":"not-a-rich-text"} 1`] = `"["Invalid rich text v2 value 'not-a-rich-text' for field \\"richTextV2Field\\" - Should be an object"]"`; diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/select-field-create-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/select-field-create-input-validation.integration-spec.ts.snap index 6e34a47d9e..4a8f02a32e 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/select-field-create-input-validation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/select-field-create-input-validation.integration-spec.ts.snap @@ -10,12 +10,12 @@ exports[`Create input validation - SELECT Gql create input - failure SELECT - sh exports[`Create input validation - SELECT Gql create input - failure SELECT - should fail with : {"selectField":true} 1`] = `"Enum "ApiInputValidationTestObjectSelectFieldEnum" cannot represent non-string value: true."`; -exports[`Create input validation - SELECT Rest create input - failure SELECT - should fail with : {"selectField":"not-a-select-option"} 1`] = `"["Invalid value 'not-a-select-option' for field \\"selectField\\""]"`; +exports[`Create input validation - SELECT Rest create input - failure SELECT - should fail with : {"selectField":"not-a-select-option"} 1`] = `"["invalid input value for enum workspace_1wgvd1injqtife6y4rvfbu3h5.\\"_apiInputValidationTestObject_selectField_enum\\": \\"not-a-select-option\\""]"`; -exports[`Create input validation - SELECT Rest create input - failure SELECT - should fail with : {"selectField":[]} 1`] = `"["Invalid string value [] for text field \\"selectField\\""]"`; +exports[`Create input validation - SELECT Rest create input - failure SELECT - should fail with : {"selectField":[]} 1`] = `"["invalid input value for enum workspace_1wgvd1injqtife6y4rvfbu3h5.\\"_apiInputValidationTestObject_selectField_enum\\": \\"\\""]"`; -exports[`Create input validation - SELECT Rest create input - failure SELECT - should fail with : {"selectField":{}} 1`] = `"["Invalid string value {} for text field \\"selectField\\""]"`; +exports[`Create input validation - SELECT Rest create input - failure SELECT - should fail with : {"selectField":{}} 1`] = `"["invalid input value for enum workspace_1wgvd1injqtife6y4rvfbu3h5.\\"_apiInputValidationTestObject_selectField_enum\\": \\"[object Object]\\""]"`; -exports[`Create input validation - SELECT Rest create input - failure SELECT - should fail with : {"selectField":1} 1`] = `"["Invalid string value 1 for text field \\"selectField\\""]"`; +exports[`Create input validation - SELECT Rest create input - failure SELECT - should fail with : {"selectField":1} 1`] = `"["invalid input value for enum workspace_1wgvd1injqtife6y4rvfbu3h5.\\"_apiInputValidationTestObject_selectField_enum\\": \\"1\\""]"`; -exports[`Create input validation - SELECT Rest create input - failure SELECT - should fail with : {"selectField":true} 1`] = `"["Invalid string value true for text field \\"selectField\\""]"`; +exports[`Create input validation - SELECT Rest create input - failure SELECT - should fail with : {"selectField":true} 1`] = `"["invalid input value for enum workspace_1wgvd1injqtife6y4rvfbu3h5.\\"_apiInputValidationTestObject_selectField_enum\\": \\"true\\""]"`; diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/text-field-create-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/text-field-create-input-validation.integration-spec.ts.snap index c1b79e36be..53b86773a6 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/text-field-create-input-validation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/text-field-create-input-validation.integration-spec.ts.snap @@ -1,21 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Create input validation - TEXT Gql create input - failure TEXT - should fail with : {"textField":[]} 1`] = `"String cannot represent a non string value: []"`; - -exports[`Create input validation - TEXT Gql create input - failure TEXT - should fail with : {"textField":{}} 1`] = `"String cannot represent a non string value: {}"`; - -exports[`Create input validation - TEXT Gql create input - failure TEXT - should fail with : {"textField":1} 1`] = `"String cannot represent a non string value: 1"`; - exports[`Create input validation - TEXT Gql create input - failure TEXT - should fail with : {"textField":null} 1`] = `"null value in column "textField" of relation "_apiInputValidationTestObject" violates not-null constraint"`; -exports[`Create input validation - TEXT Gql create input - failure TEXT - should fail with : {"textField":true} 1`] = `"String cannot represent a non string value: true"`; - -exports[`Create input validation - TEXT Rest create input - failure TEXT - should fail with : {"textField":[]} 1`] = `"["Invalid string value [] for text field \\"textField\\""]"`; - -exports[`Create input validation - TEXT Rest create input - failure TEXT - should fail with : {"textField":{}} 1`] = `"["Invalid string value {} for text field \\"textField\\""]"`; - -exports[`Create input validation - TEXT Rest create input - failure TEXT - should fail with : {"textField":1} 1`] = `"["Invalid string value 1 for text field \\"textField\\""]"`; - exports[`Create input validation - TEXT Rest create input - failure TEXT - should fail with : {"textField":null} 1`] = `"["null value in column \\"textField\\" of relation \\"_apiInputValidationTestObject\\" violates not-null constraint"]"`; - -exports[`Create input validation - TEXT Rest create input - failure TEXT - should fail with : {"textField":true} 1`] = `"["Invalid string value true for text field \\"textField\\""]"`; diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/uuid-field-create-input-validation.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/uuid-field-create-input-validation.integration-spec.ts.snap index 67be00eb88..44e0a7d722 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/uuid-field-create-input-validation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/__snapshots__/uuid-field-create-input-validation.integration-spec.ts.snap @@ -10,12 +10,12 @@ exports[`Create input validation - UUID Gql create input - failure UUID - should exports[`Create input validation - UUID Gql create input - failure UUID - should fail with : {"uuidField":true} 1`] = `"UUID must be a string"`; -exports[`Create input validation - UUID Rest create input - failure UUID - should fail with : {"uuidField":"non-uuid"} 1`] = `"["Invalid UUID value 'non-uuid' for field \\"uuidField\\""]"`; +exports[`Create input validation - UUID Rest create input - failure UUID - should fail with : {"uuidField":"non-uuid"} 1`] = `"["invalid input syntax for type uuid: \\"non-uuid\\""]"`; -exports[`Create input validation - UUID Rest create input - failure UUID - should fail with : {"uuidField":[]} 1`] = `"["Invalid UUID value [] for field \\"uuidField\\""]"`; +exports[`Create input validation - UUID Rest create input - failure UUID - should fail with : {"uuidField":[]} 1`] = `"["invalid input syntax for type uuid: \\"{}\\""]"`; -exports[`Create input validation - UUID Rest create input - failure UUID - should fail with : {"uuidField":{}} 1`] = `"["Invalid UUID value {} for field \\"uuidField\\""]"`; +exports[`Create input validation - UUID Rest create input - failure UUID - should fail with : {"uuidField":{}} 1`] = `"["invalid input syntax for type uuid: \\"{}\\""]"`; -exports[`Create input validation - UUID Rest create input - failure UUID - should fail with : {"uuidField":1} 1`] = `"["Invalid UUID value 1 for field \\"uuidField\\""]"`; +exports[`Create input validation - UUID Rest create input - failure UUID - should fail with : {"uuidField":1} 1`] = `"["invalid input syntax for type uuid: \\"1\\""]"`; -exports[`Create input validation - UUID Rest create input - failure UUID - should fail with : {"uuidField":true} 1`] = `"["Invalid UUID value true for field \\"uuidField\\""]"`; +exports[`Create input validation - UUID Rest create input - failure UUID - should fail with : {"uuidField":true} 1`] = `"["invalid input syntax for type uuid: \\"true\\""]"`; diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/address-field-create-input-validation.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/address-field-create-input-validation.integration-spec.ts index 397c89b9bf..7b78152c55 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/address-field-create-input-validation.integration-spec.ts +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/address-field-create-input-validation.integration-spec.ts @@ -1,8 +1,5 @@ -import { failingCreateInputByFieldMetadataType } from 'test/integration/graphql/suites/inputs-validation/create-validation/constants/failing-create-input-by-field-metadata-type.constant'; import { successfulCreateInputByFieldMetadataType } from 'test/integration/graphql/suites/inputs-validation/create-validation/constants/successful-create-input-by-field-metadata-type.constant'; -import { expectGqlCreateInputValidationError } from 'test/integration/graphql/suites/inputs-validation/create-validation/utils/expect-gql-create-input-validation-error.util'; import { expectGqlCreateInputValidationSuccess } from 'test/integration/graphql/suites/inputs-validation/create-validation/utils/expect-gql-create-input-validation-success.util'; -import { expectRestCreateInputValidationError } from 'test/integration/graphql/suites/inputs-validation/create-validation/utils/expect-rest-create-input-validation-error.util'; import { expectRestCreateInputValidationSuccess } from 'test/integration/graphql/suites/inputs-validation/create-validation/utils/expect-rest-create-input-validation-success.util'; import { destroyManyObjectsMetadata } from 'test/integration/graphql/suites/inputs-validation/utils/destroy-many-objects-metadata'; import { setupTestObjectsWithAllFieldTypes } from 'test/integration/graphql/suites/inputs-validation/utils/setup-test-objects-with-all-field-types.util'; @@ -13,9 +10,6 @@ const FIELD_METADATA_TYPE = FieldMetadataType.ADDRESS; const successfulTestCases = successfulCreateInputByFieldMetadataType[FIELD_METADATA_TYPE]; -const failingTestCases = - failingCreateInputByFieldMetadataType[FIELD_METADATA_TYPE]; - describe(`Create input validation - ${FIELD_METADATA_TYPE}`, () => { let objectMetadataId: string; let objectMetadataSingularName: string; @@ -74,38 +68,4 @@ describe(`Create input validation - ${FIELD_METADATA_TYPE}`, () => { }, ); }); - - describe('Gql create input - failure', () => { - it.each( - failingTestCases.map((testCase) => ({ - ...testCase, - stringifiedInput: JSON.stringify(testCase.input), - })), - )( - `${FIELD_METADATA_TYPE} - should fail with : $stringifiedInput`, - async ({ input }) => { - await expectGqlCreateInputValidationError( - objectMetadataSingularName, - input, - ); - }, - ); - }); - - describe('Rest create input - failure', () => { - it.each( - failingTestCases.map((testCase) => ({ - ...testCase, - stringifiedInput: JSON.stringify(testCase.input), - })), - )( - `${FIELD_METADATA_TYPE} - should fail with : $stringifiedInput`, - async ({ input }) => { - await expectRestCreateInputValidationError( - objectMetadataPluralName, - input, - ); - }, - ); - }); }); diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/constants/failing-create-input-by-field-metadata-type.constant.ts b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/constants/failing-create-input-by-field-metadata-type.constant.ts index 488d437e56..e6f644a2f7 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/constants/failing-create-input-by-field-metadata-type.constant.ts +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/constants/failing-create-input-by-field-metadata-type.constant.ts @@ -1,8 +1,15 @@ import { type FieldMetadataTypesToTestForCreateInputValidation } from 'test/integration/graphql/suites/inputs-validation/types/field-metadata-type-to-test'; import { FieldMetadataType } from 'twenty-shared/types'; +import { type CompositeFieldMetadataType } from 'src/engine/metadata-modules/workspace-migration/factories/composite-column-action.factory'; + export const failingCreateInputByFieldMetadataType: { - [K in FieldMetadataTypesToTestForCreateInputValidation]: { + [K in Exclude< + FieldMetadataTypesToTestForCreateInputValidation, + | CompositeFieldMetadataType + | FieldMetadataType.NUMBER + | FieldMetadataType.BOOLEAN + >]: { input: any; }[]; } = { @@ -12,49 +19,57 @@ export const failingCreateInputByFieldMetadataType: { textField: null, }, }, - { - input: { - textField: {}, - }, - }, - { - input: { - textField: [], - }, - }, - { - input: { - textField: true, - }, - }, - { - input: { - textField: 1, - }, - }, - ], - [FieldMetadataType.NUMBER]: [ - { - input: { - numberField: {}, - }, - }, - { - input: { - numberField: [], - }, - }, - { - input: { - numberField: true, - }, - }, - { - input: { - numberField: 'string', - }, - }, + // { + // input: { + // textField: {}, + // }, + // //TODO - rest api to fix, should throw + // }, + // { + // input: { + // textField: [], + // }, + // //TODO - rest api to fix, should throw + // }, + // { + // input: { + // textField: true, + // }, + // //TODO - rest api to fix, should throw + // }, + // { + // input: { + // textField: 1, + // }, + // //TODO - rest api to fix, should throw + // }, ], + // [FieldMetadataType.NUMBER]: [ + // { + // input: { + // numberField: {}, + // }, + // //TODO - rest api to fix, should throw + // }, + // { + // input: { + // numberField: [], + // }, + // //TODO - rest api to fix, should throw + // }, + // { + // input: { + // numberField: true, + // }, + // //TODO - rest api to fix, should throw + // }, + // { + // input: { + // numberField: 'string', + // }, + // // TODO - rest api to fix, should throw + // }, + // ], [FieldMetadataType.UUID]: [ { input: { @@ -110,16 +125,18 @@ export const failingCreateInputByFieldMetadataType: { }, ], [FieldMetadataType.RELATION]: [ - { - input: { - manyToOneRelationFieldId: {}, - }, - }, - { - input: { - manyToOneRelationFieldId: [], - }, - }, + // { + // input: { + // manyToOneRelationFieldId: {}, + // }, + // //TODO - rest api to fix, should throw + // }, + // { + // input: { + // manyToOneRelationFieldId: [], + // }, + // //TODO - rest api to fix, should throw + // }, { input: { manyToOneRelationFieldId: true, @@ -135,11 +152,12 @@ export const failingCreateInputByFieldMetadataType: { manyToOneRelationFieldId: 'non-uuid', }, }, - { - input: { - oneToOneRelationField: 'not-existing-field', - }, - }, + // { + // input: { + // manyToOneRelationField: 'not-existing-field', + // }, + // //TODO - rest api to fix, should throw + // }, { input: { oneToManyRelationFieldId: 'not-existing-field', @@ -152,8 +170,26 @@ export const failingCreateInputByFieldMetadataType: { rawJsonField: 'not-a-json', }, }, + // //TODO - to fix, should throw + // { + // input: { + // rawJsonField: true, + // }, + // }, + // //TODO - to fix, should throw + // { + // input: { + // rawJsonField: 1, + // }, + // }, ], [FieldMetadataType.ARRAY]: [ + // //TODO - to fix, should throw + // { + // input: { + // arrayField: 'not-an-array', + // }, + // }, { input: { arrayField: true, @@ -198,11 +234,12 @@ export const failingCreateInputByFieldMetadataType: { multiSelectField: 'not-a-select-option', }, }, - { - input: { - multiSelectField: {}, - }, - }, + // { + // input: { + // multiSelectField: {}, + // }, + // //TODO - rest api to fix, should throw + // }, { input: { multiSelectField: true, @@ -268,33 +305,39 @@ export const failingCreateInputByFieldMetadataType: { }, }, ], - [FieldMetadataType.BOOLEAN]: [ - { - input: { - booleanField: null, - }, - }, - { - input: { - booleanField: {}, - }, - }, - { - input: { - booleanField: [], - }, - }, - { - input: { - booleanField: 'string', - }, - }, - { - input: { - booleanField: 1, - }, - }, - ], + // [FieldMetadataType.BOOLEAN]: [ + // // { + // // input: { + // // booleanField: null, + // // }, + // // }, + // // { + // // input: { + // // booleanField: {}, + // // }, + // // //TODO - rest api to fix, should throw + // // }, + // // { + // // input: { + // // booleanField: [], + // // }, + // // gqlErrorMessage: 'cannot represent a non string value', + // // //TODO - rest api to fix, should throw + // // restErrorMessage: '', + // // }, + // // { + // // input: { + // // booleanField: 'string', + // // }, + // // //TODO - rest api to fix, should throw + // // }, + // // { + // // input: { + // // booleanField: 1, + // // }, + // // //TODO - rest api to fix, should throw + // // }, + // ], [FieldMetadataType.RICH_TEXT]: [ { input: { @@ -302,53 +345,74 @@ export const failingCreateInputByFieldMetadataType: { }, }, ], - [FieldMetadataType.ADDRESS]: [ - { - input: { - addressField: 'not-an-address', - }, - }, - ], - [FieldMetadataType.CURRENCY]: [ - { - input: { - currencyField: 'not-a-currency', - }, - }, - ], - [FieldMetadataType.EMAILS]: [ - { - input: { - emailsField: 'not-an-email', - }, - }, - ], - [FieldMetadataType.PHONES]: [ - { - input: { - phonesField: 'not-a-phone', - }, - }, - ], - [FieldMetadataType.FULL_NAME]: [ - { - input: { - fullNameField: 'not-a-full-name', - }, - }, - ], - [FieldMetadataType.LINKS]: [ - { - input: { - linksField: 'not-a-link', - }, - }, - ], - [FieldMetadataType.RICH_TEXT_V2]: [ - { - input: { - richTextV2Field: 'not-a-rich-text', - }, - }, - ], + // [FieldMetadataType.ADDRESS]: [ + // // { + // // input: { + // // addressField: null, + // // }, + // // //TODO - rest api to fix, should throw + // // }, + // // { + // // input: { + // // addressField: 'not-an-address', + // // }, + // // //TODO - rest api to fix, should throw + // // }, + // ], + // [FieldMetadataType.CURRENCY]: [ + // // { + // // input: { + // // currencyField: null, + // // }, + // // //TODO - rest api to fix, should throw + // // }, + // ], + // [FieldMetadataType.EMAILS]: [ + // // { + // // input: { + // // emailsField: null, + // // }, + // // //TODO - rest api to fix, should throw + // // }, + // ], + // [FieldMetadataType.PHONES]: [ + // // { + // // input: { + // // phonesField: null, + // // }, + // // //TODO - rest api to fix, should throw + // // }, + // ], + // [FieldMetadataType.FULL_NAME]: [ + // // { + // // input: { + // // fullNameField: null, + // // }, + // // //TODO - rest api to fix, should throw + // // }, + // ], + // [FieldMetadataType.LINKS]: [ + // // { + // // input: { + // // linksField: null, + // // }, + // // //TODO - rest api to fix, should throw + // // }, + // ], + // [FieldMetadataType.RICH_TEXT_V2]: [ + // // { + // // input: { + // // richTextV2Field: null, + // // }, + // // //TODO - rest api to fix, should throw + // // }, + // ], + // [FieldMetadataType.ACTOR]: [ + // // { + // // input: { + // // actorField: null, + // // }, + // // //TODO - rest api to fix, should throw + // // }, + // ], }; diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/constants/successful-create-input-by-field-metadata-type.constant.ts b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/constants/successful-create-input-by-field-metadata-type.constant.ts index 1675bf946f..7c8d94f5d1 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/constants/successful-create-input-by-field-metadata-type.constant.ts +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/constants/successful-create-input-by-field-metadata-type.constant.ts @@ -159,16 +159,6 @@ export const successfulCreateInputByFieldMetadataType: { ); }, }, - { - input: { - arrayField: 'item1', - }, - validateInput: (record: Record) => { - return ( - record.arrayField.length === 1 && record.arrayField.includes('item1') - ); - }, - }, { input: { arrayField: [], diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/currency-field-create-input-validation.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/currency-field-create-input-validation.integration-spec.ts index 19206f79e9..4463811d80 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/currency-field-create-input-validation.integration-spec.ts +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/currency-field-create-input-validation.integration-spec.ts @@ -1,8 +1,5 @@ -import { failingCreateInputByFieldMetadataType } from 'test/integration/graphql/suites/inputs-validation/create-validation/constants/failing-create-input-by-field-metadata-type.constant'; import { successfulCreateInputByFieldMetadataType } from 'test/integration/graphql/suites/inputs-validation/create-validation/constants/successful-create-input-by-field-metadata-type.constant'; -import { expectGqlCreateInputValidationError } from 'test/integration/graphql/suites/inputs-validation/create-validation/utils/expect-gql-create-input-validation-error.util'; import { expectGqlCreateInputValidationSuccess } from 'test/integration/graphql/suites/inputs-validation/create-validation/utils/expect-gql-create-input-validation-success.util'; -import { expectRestCreateInputValidationError } from 'test/integration/graphql/suites/inputs-validation/create-validation/utils/expect-rest-create-input-validation-error.util'; import { expectRestCreateInputValidationSuccess } from 'test/integration/graphql/suites/inputs-validation/create-validation/utils/expect-rest-create-input-validation-success.util'; import { destroyManyObjectsMetadata } from 'test/integration/graphql/suites/inputs-validation/utils/destroy-many-objects-metadata'; import { setupTestObjectsWithAllFieldTypes } from 'test/integration/graphql/suites/inputs-validation/utils/setup-test-objects-with-all-field-types.util'; @@ -13,9 +10,6 @@ const FIELD_METADATA_TYPE = FieldMetadataType.CURRENCY; const successfulTestCases = successfulCreateInputByFieldMetadataType[FIELD_METADATA_TYPE]; -const failingTestCases = - failingCreateInputByFieldMetadataType[FIELD_METADATA_TYPE]; - describe(`Create input validation - ${FIELD_METADATA_TYPE}`, () => { let objectMetadataId: string; let objectMetadataSingularName: string; @@ -74,38 +68,4 @@ describe(`Create input validation - ${FIELD_METADATA_TYPE}`, () => { }, ); }); - - describe('Gql create input - failure', () => { - it.each( - failingTestCases.map((testCase) => ({ - ...testCase, - stringifiedInput: JSON.stringify(testCase.input), - })), - )( - `${FIELD_METADATA_TYPE} - should fail with : $stringifiedInput`, - async ({ input }) => { - await expectGqlCreateInputValidationError( - objectMetadataSingularName, - input, - ); - }, - ); - }); - - describe('Rest create input - failure', () => { - it.each( - failingTestCases.map((testCase) => ({ - ...testCase, - stringifiedInput: JSON.stringify(testCase.input), - })), - )( - `${FIELD_METADATA_TYPE} - should fail with : $stringifiedInput`, - async ({ input }) => { - await expectRestCreateInputValidationError( - objectMetadataPluralName, - input, - ); - }, - ); - }); }); diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/emails-field-create-input-validation.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/emails-field-create-input-validation.integration-spec.ts index da2c7fd787..bb9c680b57 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/emails-field-create-input-validation.integration-spec.ts +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/emails-field-create-input-validation.integration-spec.ts @@ -1,8 +1,5 @@ -import { failingCreateInputByFieldMetadataType } from 'test/integration/graphql/suites/inputs-validation/create-validation/constants/failing-create-input-by-field-metadata-type.constant'; import { successfulCreateInputByFieldMetadataType } from 'test/integration/graphql/suites/inputs-validation/create-validation/constants/successful-create-input-by-field-metadata-type.constant'; -import { expectGqlCreateInputValidationError } from 'test/integration/graphql/suites/inputs-validation/create-validation/utils/expect-gql-create-input-validation-error.util'; import { expectGqlCreateInputValidationSuccess } from 'test/integration/graphql/suites/inputs-validation/create-validation/utils/expect-gql-create-input-validation-success.util'; -import { expectRestCreateInputValidationError } from 'test/integration/graphql/suites/inputs-validation/create-validation/utils/expect-rest-create-input-validation-error.util'; import { expectRestCreateInputValidationSuccess } from 'test/integration/graphql/suites/inputs-validation/create-validation/utils/expect-rest-create-input-validation-success.util'; import { destroyManyObjectsMetadata } from 'test/integration/graphql/suites/inputs-validation/utils/destroy-many-objects-metadata'; import { setupTestObjectsWithAllFieldTypes } from 'test/integration/graphql/suites/inputs-validation/utils/setup-test-objects-with-all-field-types.util'; @@ -13,9 +10,6 @@ const FIELD_METADATA_TYPE = FieldMetadataType.EMAILS; const successfulTestCases = successfulCreateInputByFieldMetadataType[FIELD_METADATA_TYPE]; -const failingTestCases = - failingCreateInputByFieldMetadataType[FIELD_METADATA_TYPE]; - describe(`Create input validation - ${FIELD_METADATA_TYPE}`, () => { let objectMetadataId: string; let objectMetadataSingularName: string; @@ -74,38 +68,4 @@ describe(`Create input validation - ${FIELD_METADATA_TYPE}`, () => { }, ); }); - - describe('Gql create input - failure', () => { - it.each( - failingTestCases.map((testCase) => ({ - ...testCase, - stringifiedInput: JSON.stringify(testCase.input), - })), - )( - `${FIELD_METADATA_TYPE} - should fail with : $stringifiedInput`, - async ({ input }) => { - await expectGqlCreateInputValidationError( - objectMetadataSingularName, - input, - ); - }, - ); - }); - - describe('Rest create input - failure', () => { - it.each( - failingTestCases.map((testCase) => ({ - ...testCase, - stringifiedInput: JSON.stringify(testCase.input), - })), - )( - `${FIELD_METADATA_TYPE} - should fail with : $stringifiedInput`, - async ({ input }) => { - await expectRestCreateInputValidationError( - objectMetadataPluralName, - input, - ); - }, - ); - }); }); diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/full-name-field-create-input-validation.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/full-name-field-create-input-validation.integration-spec.ts index 8c73606720..94b423e406 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/full-name-field-create-input-validation.integration-spec.ts +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/full-name-field-create-input-validation.integration-spec.ts @@ -1,8 +1,5 @@ -import { failingCreateInputByFieldMetadataType } from 'test/integration/graphql/suites/inputs-validation/create-validation/constants/failing-create-input-by-field-metadata-type.constant'; import { successfulCreateInputByFieldMetadataType } from 'test/integration/graphql/suites/inputs-validation/create-validation/constants/successful-create-input-by-field-metadata-type.constant'; -import { expectGqlCreateInputValidationError } from 'test/integration/graphql/suites/inputs-validation/create-validation/utils/expect-gql-create-input-validation-error.util'; import { expectGqlCreateInputValidationSuccess } from 'test/integration/graphql/suites/inputs-validation/create-validation/utils/expect-gql-create-input-validation-success.util'; -import { expectRestCreateInputValidationError } from 'test/integration/graphql/suites/inputs-validation/create-validation/utils/expect-rest-create-input-validation-error.util'; import { expectRestCreateInputValidationSuccess } from 'test/integration/graphql/suites/inputs-validation/create-validation/utils/expect-rest-create-input-validation-success.util'; import { destroyManyObjectsMetadata } from 'test/integration/graphql/suites/inputs-validation/utils/destroy-many-objects-metadata'; import { setupTestObjectsWithAllFieldTypes } from 'test/integration/graphql/suites/inputs-validation/utils/setup-test-objects-with-all-field-types.util'; @@ -13,9 +10,6 @@ const FIELD_METADATA_TYPE = FieldMetadataType.FULL_NAME; const successfulTestCases = successfulCreateInputByFieldMetadataType[FIELD_METADATA_TYPE]; -const failingTestCases = - failingCreateInputByFieldMetadataType[FIELD_METADATA_TYPE]; - describe(`Create input validation - ${FIELD_METADATA_TYPE}`, () => { let objectMetadataId: string; let objectMetadataSingularName: string; @@ -74,38 +68,4 @@ describe(`Create input validation - ${FIELD_METADATA_TYPE}`, () => { }, ); }); - - describe('Gql create input - failure', () => { - it.each( - failingTestCases.map((testCase) => ({ - ...testCase, - stringifiedInput: JSON.stringify(testCase.input), - })), - )( - `${FIELD_METADATA_TYPE} - should fail with : $stringifiedInput`, - async ({ input }) => { - await expectGqlCreateInputValidationError( - objectMetadataSingularName, - input, - ); - }, - ); - }); - - describe('Rest create input - failure', () => { - it.each( - failingTestCases.map((testCase) => ({ - ...testCase, - stringifiedInput: JSON.stringify(testCase.input), - })), - )( - `${FIELD_METADATA_TYPE} - should fail with : $stringifiedInput`, - async ({ input }) => { - await expectRestCreateInputValidationError( - objectMetadataPluralName, - input, - ); - }, - ); - }); }); diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/links-field-create-input-validation.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/links-field-create-input-validation.integration-spec.ts index f0db6d3722..cc17c07d2d 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/links-field-create-input-validation.integration-spec.ts +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/links-field-create-input-validation.integration-spec.ts @@ -1,8 +1,5 @@ -import { failingCreateInputByFieldMetadataType } from 'test/integration/graphql/suites/inputs-validation/create-validation/constants/failing-create-input-by-field-metadata-type.constant'; import { successfulCreateInputByFieldMetadataType } from 'test/integration/graphql/suites/inputs-validation/create-validation/constants/successful-create-input-by-field-metadata-type.constant'; -import { expectGqlCreateInputValidationError } from 'test/integration/graphql/suites/inputs-validation/create-validation/utils/expect-gql-create-input-validation-error.util'; import { expectGqlCreateInputValidationSuccess } from 'test/integration/graphql/suites/inputs-validation/create-validation/utils/expect-gql-create-input-validation-success.util'; -import { expectRestCreateInputValidationError } from 'test/integration/graphql/suites/inputs-validation/create-validation/utils/expect-rest-create-input-validation-error.util'; import { expectRestCreateInputValidationSuccess } from 'test/integration/graphql/suites/inputs-validation/create-validation/utils/expect-rest-create-input-validation-success.util'; import { destroyManyObjectsMetadata } from 'test/integration/graphql/suites/inputs-validation/utils/destroy-many-objects-metadata'; import { setupTestObjectsWithAllFieldTypes } from 'test/integration/graphql/suites/inputs-validation/utils/setup-test-objects-with-all-field-types.util'; @@ -13,9 +10,6 @@ const FIELD_METADATA_TYPE = FieldMetadataType.LINKS; const successfulTestCases = successfulCreateInputByFieldMetadataType[FIELD_METADATA_TYPE]; -const failingTestCases = - failingCreateInputByFieldMetadataType[FIELD_METADATA_TYPE]; - describe(`Create input validation - ${FIELD_METADATA_TYPE}`, () => { let objectMetadataId: string; let objectMetadataSingularName: string; @@ -74,38 +68,4 @@ describe(`Create input validation - ${FIELD_METADATA_TYPE}`, () => { }, ); }); - - describe('Gql create input - failure', () => { - it.each( - failingTestCases.map((testCase) => ({ - ...testCase, - stringifiedInput: JSON.stringify(testCase.input), - })), - )( - `${FIELD_METADATA_TYPE} - should fail with : $stringifiedInput`, - async ({ input }) => { - await expectGqlCreateInputValidationError( - objectMetadataSingularName, - input, - ); - }, - ); - }); - - describe('Rest create input - failure', () => { - it.each( - failingTestCases.map((testCase) => ({ - ...testCase, - stringifiedInput: JSON.stringify(testCase.input), - })), - )( - `${FIELD_METADATA_TYPE} - should fail with : $stringifiedInput`, - async ({ input }) => { - await expectRestCreateInputValidationError( - objectMetadataPluralName, - input, - ); - }, - ); - }); }); diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/phones-field-create-input-validation.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/phones-field-create-input-validation.integration-spec.ts index d89d991d03..9d21b5b9d1 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/phones-field-create-input-validation.integration-spec.ts +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/phones-field-create-input-validation.integration-spec.ts @@ -1,8 +1,5 @@ -import { failingCreateInputByFieldMetadataType } from 'test/integration/graphql/suites/inputs-validation/create-validation/constants/failing-create-input-by-field-metadata-type.constant'; import { successfulCreateInputByFieldMetadataType } from 'test/integration/graphql/suites/inputs-validation/create-validation/constants/successful-create-input-by-field-metadata-type.constant'; -import { expectGqlCreateInputValidationError } from 'test/integration/graphql/suites/inputs-validation/create-validation/utils/expect-gql-create-input-validation-error.util'; import { expectGqlCreateInputValidationSuccess } from 'test/integration/graphql/suites/inputs-validation/create-validation/utils/expect-gql-create-input-validation-success.util'; -import { expectRestCreateInputValidationError } from 'test/integration/graphql/suites/inputs-validation/create-validation/utils/expect-rest-create-input-validation-error.util'; import { expectRestCreateInputValidationSuccess } from 'test/integration/graphql/suites/inputs-validation/create-validation/utils/expect-rest-create-input-validation-success.util'; import { destroyManyObjectsMetadata } from 'test/integration/graphql/suites/inputs-validation/utils/destroy-many-objects-metadata'; import { setupTestObjectsWithAllFieldTypes } from 'test/integration/graphql/suites/inputs-validation/utils/setup-test-objects-with-all-field-types.util'; @@ -12,8 +9,6 @@ const FIELD_METADATA_TYPE = FieldMetadataType.PHONES; const successfulTestCases = successfulCreateInputByFieldMetadataType[FIELD_METADATA_TYPE]; -const failingTestCases = - failingCreateInputByFieldMetadataType[FIELD_METADATA_TYPE]; describe(`Create input validation - ${FIELD_METADATA_TYPE}`, () => { let objectMetadataId: string; @@ -73,38 +68,4 @@ describe(`Create input validation - ${FIELD_METADATA_TYPE}`, () => { }, ); }); - - describe('Gql create input - failure', () => { - it.each( - failingTestCases.map((testCase) => ({ - ...testCase, - stringifiedInput: JSON.stringify(testCase.input), - })), - )( - `${FIELD_METADATA_TYPE} - should fail with : $stringifiedInput`, - async ({ input }) => { - await expectGqlCreateInputValidationError( - objectMetadataSingularName, - input, - ); - }, - ); - }); - - describe('Rest create input - failure', () => { - it.each( - failingTestCases.map((testCase) => ({ - ...testCase, - stringifiedInput: JSON.stringify(testCase.input), - })), - )( - `${FIELD_METADATA_TYPE} - should fail with : $stringifiedInput`, - async ({ input }) => { - await expectRestCreateInputValidationError( - objectMetadataPluralName, - input, - ); - }, - ); - }); }); diff --git a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/rich-text-v2-field-create-input-validation.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/rich-text-v2-field-create-input-validation.integration-spec.ts index 6248325c02..400300efc2 100644 --- a/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/rich-text-v2-field-create-input-validation.integration-spec.ts +++ b/packages/twenty-server/test/integration/graphql/suites/inputs-validation/create-validation/rich-text-v2-field-create-input-validation.integration-spec.ts @@ -1,8 +1,5 @@ -import { failingCreateInputByFieldMetadataType } from 'test/integration/graphql/suites/inputs-validation/create-validation/constants/failing-create-input-by-field-metadata-type.constant'; import { successfulCreateInputByFieldMetadataType } from 'test/integration/graphql/suites/inputs-validation/create-validation/constants/successful-create-input-by-field-metadata-type.constant'; -import { expectGqlCreateInputValidationError } from 'test/integration/graphql/suites/inputs-validation/create-validation/utils/expect-gql-create-input-validation-error.util'; import { expectGqlCreateInputValidationSuccess } from 'test/integration/graphql/suites/inputs-validation/create-validation/utils/expect-gql-create-input-validation-success.util'; -import { expectRestCreateInputValidationError } from 'test/integration/graphql/suites/inputs-validation/create-validation/utils/expect-rest-create-input-validation-error.util'; import { expectRestCreateInputValidationSuccess } from 'test/integration/graphql/suites/inputs-validation/create-validation/utils/expect-rest-create-input-validation-success.util'; import { destroyManyObjectsMetadata } from 'test/integration/graphql/suites/inputs-validation/utils/destroy-many-objects-metadata'; import { setupTestObjectsWithAllFieldTypes } from 'test/integration/graphql/suites/inputs-validation/utils/setup-test-objects-with-all-field-types.util'; @@ -13,9 +10,6 @@ const FIELD_METADATA_TYPE = FieldMetadataType.RICH_TEXT_V2; const successfulTestCases = successfulCreateInputByFieldMetadataType[FIELD_METADATA_TYPE]; -const failingTestCases = - failingCreateInputByFieldMetadataType[FIELD_METADATA_TYPE]; - describe(`Create input validation - ${FIELD_METADATA_TYPE}`, () => { let objectMetadataId: string; let objectMetadataSingularName: string; @@ -74,38 +68,4 @@ describe(`Create input validation - ${FIELD_METADATA_TYPE}`, () => { }, ); }); - - describe('Gql create input - failure', () => { - it.each( - failingTestCases.map((testCase) => ({ - ...testCase, - stringifiedInput: JSON.stringify(testCase.input), - })), - )( - `${FIELD_METADATA_TYPE} - should fail with : $stringifiedInput`, - async ({ input }) => { - await expectGqlCreateInputValidationError( - objectMetadataSingularName, - input, - ); - }, - ); - }); - - describe('Rest create input - failure', () => { - it.each( - failingTestCases.map((testCase) => ({ - ...testCase, - stringifiedInput: JSON.stringify(testCase.input), - })), - )( - `${FIELD_METADATA_TYPE} - should fail with : $stringifiedInput`, - async ({ input }) => { - await expectRestCreateInputValidationError( - objectMetadataPluralName, - input, - ); - }, - ); - }); }); diff --git a/packages/twenty-server/test/integration/rest/suites/rest-api-core-create-one.integration-spec.ts b/packages/twenty-server/test/integration/rest/suites/rest-api-core-create-one.integration-spec.ts index fa04e0d5ef..a5e95ae334 100644 --- a/packages/twenty-server/test/integration/rest/suites/rest-api-core-create-one.integration-spec.ts +++ b/packages/twenty-server/test/integration/rest/suites/rest-api-core-create-one.integration-spec.ts @@ -208,7 +208,7 @@ describe('Core REST API Create One endpoint', () => { .expect(400) .expect((res) => { expect(res.body.messages[0]).toMatch( - 'Invalid value \'INVALID_ENUM_VALUE\' for field "stage"', + /invalid input value for enum workspace_[a-z0-9]+\.opportunity_stage_enum: "INVALID_ENUM_VALUE"/, ); expect(res.body.error).toBe('BadRequestException'); }); diff --git a/packages/twenty-server/test/integration/rest/suites/rest-api-core-find-many.integration-spec.ts b/packages/twenty-server/test/integration/rest/suites/rest-api-core-find-many.integration-spec.ts index bb8736ccee..d1896b1a35 100644 --- a/packages/twenty-server/test/integration/rest/suites/rest-api-core-find-many.integration-spec.ts +++ b/packages/twenty-server/test/integration/rest/suites/rest-api-core-find-many.integration-spec.ts @@ -53,7 +53,6 @@ describe('Core REST API Find Many endpoint', () => { companyId: TEST_COMPANY_1_ID, }, }); - index++; } }); @@ -62,9 +61,7 @@ describe('Core REST API Find Many endpoint', () => { const response = await makeRestAPIRequest({ method: 'get', path: '/people', - }); - - expect(response.status).toBe(200); + }).expect(200); const people = response.body.data.people; const pageInfo = response.body.pageInfo;