From 98a9ae2a0e4798a38b0f6a90ee1e6e48662dc399 Mon Sep 17 00:00:00 2001 From: Paul Rastoin <45004772+prastoin@users.noreply.github.com> Date: Fri, 2 Jan 2026 14:16:01 +0100 Subject: [PATCH] Migrate view filter group to v2 (#16876) ## Introduction On the side hanlded PR with a // agents on another repo Made several iterations to fix behavior and direction Find below auto-generated PR description closes https://github.com/twentyhq/core-team-issues/issues/2037 Created generic tooling for entity circular dep checking, will be useful for permissions validation too @Weiko ## Migrate `viewFilterGroup` entity to v2 flat architecture ### Summary Migrates the `viewFilterGroup` entity from v1 to the v2 flat entity architecture, following the established patterns for other v2 entities like `viewFilter`, `view`, and `viewField`. ### Changes **Types & Constants** - Added `FlatViewFilterGroup` and `FlatViewFilterGroupMaps` types - Added editable properties constant for `viewFilterGroup` - Registered `viewFilterGroup` in `ALL_METADATA_NAME`, `ALL_METADATA_RELATION_PROPERTIES`, `ALL_METADATA_MANY_TO_ONE_RELATIONS`, and related constants **Cache Service** - Created `WorkspaceFlatViewFilterGroupMapCacheService` with proper relation loading for `viewFilters` and `childViewFilterGroups` - Updated `WorkspaceFlatViewMapCacheService` to load `viewFilterGroups` relation **Builder & Validator** - Created `WorkspaceMigrationV2ViewFilterGroupActionsBuilderService` - Created `FlatViewFilterGroupValidatorService` with creation, update, and deletion validation - Integrated validation into the orchestrator service (runs before `viewFilter` validation) **Action Handlers** - Created create, update, and delete action handlers for `viewFilterGroup` **Service Migration** - Rewrote `ViewFilterGroupService` to use v2 migration pattern with `WorkspaceMigrationValidateBuildAndRunService` - Created utility functions for transforming DTOs to flat entities **Database Migration** - Added migration to make `parentViewFilterGroupId` foreign key deferrable (handles self-referential parent/child insertions) **ViewFilter Integration** - Added `viewFilterGroupId` validation in `FlatViewFilterValidatorService` - Updated `viewFilter` many-to-one relations to include `viewFilterGroup` **Tests** - Added integration tests for successful creation, update, deletion, and destruction - Added failing test cases for non-existent entities and invalid references - Added failing test for `viewFilter` creation with non-existent `viewFilterGroupId` ### Breaking Changes None - existing API contracts are preserved. --- .../hooks/useMetadataErrorHandler.ts | 3 +- ...grate-standard-invalid-entities.command.ts | 16 +- ...0-makeViewFilterGroupParentFkDeferrable.ts | 29 ++ .../message-queue/drivers/sync.driver.ts | 12 +- ...rties-to-compare-and-stringify.constant.ts | 9 + ...metadata-many-to-one-relations.constant.ts | 14 + ...-metadata-relations-properties.constant.ts | 8 + ...quired-metadata-for-validation.constant.ts | 5 + ...ny-or-all-flat-entity-maps-cache.module.ts | 5 + .../all-flat-entity-types-by-metadata-name.ts | 16 + ...lter-group-editable-properties.constant.ts | 7 + ...lat-view-filter-group-map-cache.service.ts | 75 ++++ .../types/flat-view-filter-group-maps.type.ts | 4 + .../types/flat-view-filter-group.type.ts | 4 + ...o-flat-view-filter-group-to-create.util.ts | 45 ++ ...to-flat-view-filter-group-or-throw.util.ts | 40 ++ ...to-flat-view-filter-group-or-throw.util.ts | 37 ++ ...ew-filter-group-to-update-or-throw.util.ts | 51 +++ ...p-entity-to-flat-view-filter-group.util.ts | 33 ++ .../types/flat-view-filter.type.ts | 5 +- .../flat-view/flat-view.module.ts | 2 + .../workspace-flat-view-map-cache.service.ts | 65 ++- .../flat-view/types/flat-view.type.ts | 4 +- ...-view-input-to-flat-view-to-create.util.ts | 1 + .../from-view-entity-to-flat-view.util.ts | 4 + .../view-filter-group.controller.ts | 28 +- .../inputs/create-view-filter-group.input.ts | 2 +- .../inputs/delete-view-filter-group.input.ts | 15 + .../inputs/destroy-view-filter-group.input.ts | 15 + .../exceptions/view-filter-group.exception.ts | 2 + .../resolvers/view-filter-group.resolver.ts | 28 +- .../tests/view-filter-group.service.spec.ts | 226 +--------- .../services/view-filter-group.service.ts | 399 +++++++++++------- ...ter-group-to-view-filter-group-dto.util.ts | 15 + .../view-filter-group.module.ts | 6 + .../services/view-filter.service.ts | 5 - ...view-graphql-api-exception-handler.util.ts | 8 + .../types/workspace-cache-key.type.ts | 1 + ...plication-all-flat-entity-maps.constant.ts | 3 + ...create-standard-view-flat-metadata.util.ts | 1 + ...metadata-validation-response-error.util.ts | 1 + ...ce-migration-build-orchestrator.service.ts | 60 ++- ...at-entity-circular-dependency.util.spec.ts | 245 +++++++++++ ...te-flat-entity-circular-dependency.util.ts | 107 +++++ ...ration-view-filter-group-action-v2.type.ts | 26 ++ ...ew-filter-group-actions-builder.service.ts | 113 +++++ ...lat-view-filter-group-validator.service.ts | 260 ++++++++++++ .../flat-view-filter-validator.service.ts | 40 +- ...ace-migration-builder-validators.module.ts | 3 + .../workspace-migration-builder-v2.module.ts | 3 + ...iew-filter-group-action-handler.service.ts | 59 +++ ...iew-filter-group-action-handler.service.ts | 62 +++ ...iew-filter-group-action-handler.service.ts | 73 ++++ ...migration-runner-action-handlers.module.ts | 7 + .../constants/view-gql-fields.constants.ts | 1 + ...ny-view-fields-v2.integration-spec.ts.snap | 2 + ...ate-view-field-v2.integration-spec.ts.snap | 6 + ...ect-on-view-field.integration-spec.ts.snap | 10 + ...-filter-group-resolver.integration-spec.ts | 3 - ...ng-agent-creation.integration-spec.ts.snap | 14 + ...ng-agent-deletion.integration-spec.ts.snap | 2 + ...ling-agent-update.integration-spec.ts.snap | 18 + ...ne-field-metadata.integration-spec.ts.snap | 2 + ...ne-field-metadata.integration-spec.ts.snap | 6 + ...ne-field-metadata.integration-spec.ts.snap | 4 + ...-metadata-enum-v2.integration-spec.ts.snap | 140 ++++++ ...field-metadata-v2.integration-spec.ts.snap | 136 ++++++ ...morph-relation-v2.integration-spec.ts.snap | 4 + ...morph-relation-v2.integration-spec.ts.snap | 34 ++ ...relation-creation.integration-spec.ts.snap | 64 +++ ...a-relation-update.integration-spec.ts.snap | 2 + ...ue-field-metadata.integration-spec.ts.snap | 4 + ...bject-metadata-v2.integration-spec.ts.snap | 34 ++ ...e-object-metadata.integration-spec.ts.snap | 8 + ...yout-tab-creation.integration-spec.ts.snap | 2 + ...t-widget-creation.integration-spec.ts.snap | 2 + ...ing-role-creation.integration-spec.ts.snap | 10 + ...iling-role-update.integration-spec.ts.snap | 14 + ...rcular-dependency.integration-spec.ts.snap | 150 +++++++ ...er-group-creation.integration-spec.ts.snap | 237 +++++++++++ ...er-group-deletion.integration-spec.ts.snap | 12 + ...ter-group-destroy.integration-spec.ts.snap | 12 + ...rcular-dependency.integration-spec.ts.snap | 145 +++++++ ...lter-group-update.integration-spec.ts.snap | 84 ++++ ...on-circular-dependency.integration-spec.ts | 136 ++++++ ...-filter-group-creation.integration-spec.ts | 125 ++++++ ...-filter-group-deletion.integration-spec.ts | 37 ++ ...w-filter-group-destroy.integration-spec.ts | 37 ++ ...te-circular-dependency.integration-spec.ts | 152 +++++++ ...ew-filter-group-update.integration-spec.ts | 129 ++++++ ...-filter-group-creation.integration-spec.ts | 148 +++++++ ...-filter-group-deletion.integration-spec.ts | 82 ++++ ...w-filter-group-destroy.integration-spec.ts | 75 ++++ ...ew-filter-group-update.integration-spec.ts | 112 +++++ ...re-view-filter-group-query-factory.util.ts | 21 + .../create-one-core-view-filter-group.util.ts | 40 ++ ...re-view-filter-group-query-factory.util.ts | 16 + .../delete-one-core-view-filter-group.util.ts | 35 ++ ...re-view-filter-group-query-factory.util.ts | 16 + ...destroy-one-core-view-filter-group.util.ts | 36 ++ ...re-view-filter-group-query-factory.util.ts | 25 ++ .../update-one-core-view-filter-group.util.ts | 44 ++ ...w-filter-creation.integration-spec.ts.snap | 227 ++++++++++ ...g-view-filter-creation.integration-spec.ts | 122 ++++++ .../view-group.integration-spec.ts.snap | 2 + .../metadata/all-metadata-name.constant.ts | 1 + 106 files changed, 4648 insertions(+), 439 deletions(-) create mode 100644 packages/twenty-server/src/database/typeorm/core/migrations/common/1767100000000-makeViewFilterGroupParentFkDeferrable.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-view-filter-group/constants/flat-view-filter-group-editable-properties.constant.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-view-filter-group/services/workspace-flat-view-filter-group-map-cache.service.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-view-filter-group/types/flat-view-filter-group-maps.type.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-view-filter-group/types/flat-view-filter-group.type.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-view-filter-group/utils/from-create-view-filter-group-input-to-flat-view-filter-group-to-create.util.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-view-filter-group/utils/from-delete-view-filter-group-input-to-flat-view-filter-group-or-throw.util.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-view-filter-group/utils/from-destroy-view-filter-group-input-to-flat-view-filter-group-or-throw.util.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-view-filter-group/utils/from-update-view-filter-group-input-to-flat-view-filter-group-to-update-or-throw.util.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-view-filter-group/utils/from-view-filter-group-entity-to-flat-view-filter-group.util.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/view-filter-group/dtos/inputs/delete-view-filter-group.input.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/view-filter-group/dtos/inputs/destroy-view-filter-group.input.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/view-filter-group/utils/from-flat-view-filter-group-to-view-filter-group-dto.util.ts create mode 100644 packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/utils/__tests__/validate-flat-entity-circular-dependency.util.spec.ts create mode 100644 packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/utils/validate-flat-entity-circular-dependency.util.ts create mode 100644 packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view-filter-group/types/workspace-migration-view-filter-group-action-v2.type.ts create mode 100644 packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view-filter-group/workspace-migration-v2-view-filter-group-actions-builder.service.ts create mode 100644 packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-view-filter-group-validator.service.ts create mode 100644 packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/view-filter-group/services/create-view-filter-group-action-handler.service.ts create mode 100644 packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/view-filter-group/services/delete-view-filter-group-action-handler.service.ts create mode 100644 packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/view-filter-group/services/update-view-filter-group-action-handler.service.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/view-filter-group/__snapshots__/failing-view-filter-group-creation-circular-dependency.integration-spec.ts.snap create mode 100644 packages/twenty-server/test/integration/metadata/suites/view-filter-group/__snapshots__/failing-view-filter-group-creation.integration-spec.ts.snap create mode 100644 packages/twenty-server/test/integration/metadata/suites/view-filter-group/__snapshots__/failing-view-filter-group-deletion.integration-spec.ts.snap create mode 100644 packages/twenty-server/test/integration/metadata/suites/view-filter-group/__snapshots__/failing-view-filter-group-destroy.integration-spec.ts.snap create mode 100644 packages/twenty-server/test/integration/metadata/suites/view-filter-group/__snapshots__/failing-view-filter-group-update-circular-dependency.integration-spec.ts.snap create mode 100644 packages/twenty-server/test/integration/metadata/suites/view-filter-group/__snapshots__/failing-view-filter-group-update.integration-spec.ts.snap create mode 100644 packages/twenty-server/test/integration/metadata/suites/view-filter-group/failing-view-filter-group-creation-circular-dependency.integration-spec.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/view-filter-group/failing-view-filter-group-creation.integration-spec.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/view-filter-group/failing-view-filter-group-deletion.integration-spec.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/view-filter-group/failing-view-filter-group-destroy.integration-spec.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/view-filter-group/failing-view-filter-group-update-circular-dependency.integration-spec.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/view-filter-group/failing-view-filter-group-update.integration-spec.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/view-filter-group/successful-view-filter-group-creation.integration-spec.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/view-filter-group/successful-view-filter-group-deletion.integration-spec.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/view-filter-group/successful-view-filter-group-destroy.integration-spec.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/view-filter-group/successful-view-filter-group-update.integration-spec.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/view-filter-group/utils/create-core-view-filter-group-query-factory.util.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/view-filter-group/utils/create-one-core-view-filter-group.util.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/view-filter-group/utils/delete-core-view-filter-group-query-factory.util.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/view-filter-group/utils/delete-one-core-view-filter-group.util.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/view-filter-group/utils/destroy-core-view-filter-group-query-factory.util.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/view-filter-group/utils/destroy-one-core-view-filter-group.util.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/view-filter-group/utils/update-core-view-filter-group-query-factory.util.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/view-filter-group/utils/update-one-core-view-filter-group.util.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/view-filter/__snapshots__/failing-view-filter-creation.integration-spec.ts.snap create mode 100644 packages/twenty-server/test/integration/metadata/suites/view-filter/failing-view-filter-creation.integration-spec.ts diff --git a/packages/twenty-front/src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts b/packages/twenty-front/src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts index 3e39dabc5d..8ce8af7d3a 100644 --- a/packages/twenty-front/src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts +++ b/packages/twenty-front/src/modules/metadata-error-handler/hooks/useMetadataErrorHandler.ts @@ -2,12 +2,12 @@ import { type ApolloError } from '@apollo/client'; import { t } from '@lingui/core/macro'; import { useCallback } from 'react'; +import { classifyMetadataError } from '@/metadata-error-handler/utils/classify-metadata-error.util'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { type AllMetadataName, WorkspaceMigrationV2ExceptionCode, } from 'twenty-shared/metadata'; -import { classifyMetadataError } from '@/metadata-error-handler/utils/classify-metadata-error.util'; export const useMetadataErrorHandler = () => { const { enqueueErrorSnackBar } = useSnackBar(); @@ -31,6 +31,7 @@ export const useMetadataErrorHandler = () => { pageLayoutWidget: t`page layout widget`, rowLevelPermissionPredicate: t`row level permission predicate`, rowLevelPermissionPredicateGroup: t`row level permission predicate group`, + viewFilterGroup: t`view filter group`, } as const satisfies Record; const handleMetadataError = useCallback( diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-13/1-13-migrate-standard-invalid-entities.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-13/1-13-migrate-standard-invalid-entities.command.ts index 286405c683..8c35e60c2b 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-13/1-13-migrate-standard-invalid-entities.command.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/1-13/1-13-migrate-standard-invalid-entities.command.ts @@ -3,27 +3,27 @@ import { InjectRepository } from '@nestjs/typeorm'; import { Command } from 'nest-commander'; import { STANDARD_OBJECT_IDS } from 'twenty-shared/metadata'; import { - FieldMetadataComplexOption, - FieldMetadataDefaultValue, + type FieldMetadataComplexOption, + type FieldMetadataDefaultValue, FieldMetadataType, - FromTo, + type FromTo, } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; import { type Repository } from 'typeorm'; import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner'; -import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner'; +import { type RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner'; import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; import { FieldMetadataService } from 'src/engine/metadata-modules/field-metadata/services/field-metadata.service'; -import { MetadataFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/metadata-flat-entity-maps.type'; +import { type MetadataFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/metadata-flat-entity-maps.type'; import { findManyFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-many-flat-entity-by-id-in-flat-entity-maps-or-throw.util'; import { isFlatFieldMetadataOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util'; -import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type'; +import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type'; import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager'; import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service'; -import { AllStandardObjectFieldName } from 'src/engine/workspace-manager/twenty-standard-application/types/all-standard-object-field-name.type'; -import { AllStandardObjectName } from 'src/engine/workspace-manager/twenty-standard-application/types/all-standard-object-name.type'; +import { type AllStandardObjectFieldName } from 'src/engine/workspace-manager/twenty-standard-application/types/all-standard-object-field-name.type'; +import { type AllStandardObjectName } from 'src/engine/workspace-manager/twenty-standard-application/types/all-standard-object-name.type'; import { CALENDAR_EVENT_STANDARD_FIELD_IDS, MESSAGE_CHANNEL_STANDARD_FIELD_IDS, diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/common/1767100000000-makeViewFilterGroupParentFkDeferrable.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1767100000000-makeViewFilterGroupParentFkDeferrable.ts new file mode 100644 index 0000000000..ba5e598150 --- /dev/null +++ b/packages/twenty-server/src/database/typeorm/core/migrations/common/1767100000000-makeViewFilterGroupParentFkDeferrable.ts @@ -0,0 +1,29 @@ +import { type MigrationInterface, type QueryRunner } from 'typeorm'; + +export class MakeViewFilterGroupParentFkDeferrable1767100000000 + implements MigrationInterface +{ + name = 'MakeViewFilterGroupParentFkDeferrable1767100000000'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "core"."viewFilterGroup" DROP CONSTRAINT IF EXISTS "FK_6aa17342705ae5526de377bf7ed"`, + ); + + // Recreate parentViewFilterGroupId FK as DEFERRABLE INITIALLY DEFERRED + // Needed because parent/child viewFilterGroups may be inserted in any order + await queryRunner.query( + `ALTER TABLE "core"."viewFilterGroup" ADD CONSTRAINT "FK_6aa17342705ae5526de377bf7ed" FOREIGN KEY ("parentViewFilterGroupId") REFERENCES "core"."viewFilterGroup"("id") ON DELETE CASCADE ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "core"."viewFilterGroup" DROP CONSTRAINT IF EXISTS "FK_6aa17342705ae5526de377bf7ed"`, + ); + + await queryRunner.query( + `ALTER TABLE "core"."viewFilterGroup" ADD CONSTRAINT "FK_6aa17342705ae5526de377bf7ed" FOREIGN KEY ("parentViewFilterGroupId") REFERENCES "core"."viewFilterGroup"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + } +} diff --git a/packages/twenty-server/src/engine/core-modules/message-queue/drivers/sync.driver.ts b/packages/twenty-server/src/engine/core-modules/message-queue/drivers/sync.driver.ts index 707b9bd268..3b0fc358a3 100644 --- a/packages/twenty-server/src/engine/core-modules/message-queue/drivers/sync.driver.ts +++ b/packages/twenty-server/src/engine/core-modules/message-queue/drivers/sync.driver.ts @@ -1,10 +1,10 @@ import { Logger } from '@nestjs/common'; -import { - type MessageQueueJobData, - type MessageQueueJob, -} from 'src/engine/core-modules/message-queue/interfaces/message-queue-job.interface'; import { type MessageQueueDriver } from 'src/engine/core-modules/message-queue/drivers/interfaces/message-queue-driver.interface'; +import { + type MessageQueueJob, + type MessageQueueJobData, +} from 'src/engine/core-modules/message-queue/interfaces/message-queue-job.interface'; import { type MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants'; @@ -64,7 +64,9 @@ export class SyncDriver implements MessageQueueDriver { if (worker) { await worker(job); } else { - this.logger.error(`No handler found for job: ${queueName}`); + if (process.env.NODE_ENV !== 'test') { + this.logger.error(`No handler found for job: ${queueName}`); + } } } } diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-flat-entity-properties-to-compare-and-stringify.constant.ts b/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-flat-entity-properties-to-compare-and-stringify.constant.ts index 8c06edf766..b797cf9a2b 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-flat-entity-properties-to-compare-and-stringify.constant.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-flat-entity-properties-to-compare-and-stringify.constant.ts @@ -14,6 +14,7 @@ import { FLAT_ROLE_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat- import { FLAT_ROW_LEVEL_PERMISSION_PREDICATE_GROUP_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-row-level-permission-predicate-group/constants/flat-row-level-permission-predicate-group-editable-properties.constant'; import { FLAT_ROW_LEVEL_PERMISSION_PREDICATE_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-row-level-permission-predicate/constants/flat-row-level-permission-predicate-editable-properties.constant'; import { FLAT_VIEW_FIELD_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-view-field/constants/flat-view-field-editable-properties.constant'; +import { FLAT_VIEW_FILTER_GROUP_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-view-filter-group/constants/flat-view-filter-group-editable-properties.constant'; import { FLAT_VIEW_FILTER_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-view-filter/constants/flat-view-filter-editable-properties.constant'; import { FLAT_VIEW_GROUP_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-view-group/constants/flat-view-group-editable-properties.constant'; import { FLAT_VIEW_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-view/constants/flat-view-editable-properties.constant'; @@ -148,6 +149,14 @@ export const ALL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY = { ], propertiesToStringify: [], }, + viewFilterGroup: { + propertiesToCompare: [ + 'viewId', + 'deletedAt', + ...FLAT_VIEW_FILTER_GROUP_EDITABLE_PROPERTIES, + ], + propertiesToStringify: [], + }, } as const satisfies { [P in AllMetadataName]: OneFlatEntityConfiguration

; }; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-metadata-many-to-one-relations.constant.ts b/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-metadata-many-to-one-relations.constant.ts index 18f4b947f6..28cfe61a5c 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-metadata-many-to-one-relations.constant.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-metadata-many-to-one-relations.constant.ts @@ -100,6 +100,10 @@ export const ALL_METADATA_RELATED_METADATA_BY_FOREIGN_KEY = { metadataName: 'fieldMetadata', flatEntityForeignKeyAggregator: 'viewFilterIds', }, + viewFilterGroupId: { + metadataName: 'viewFilterGroup', + flatEntityForeignKeyAggregator: 'viewFilterIds', + }, }, role: {}, roleTarget: { @@ -124,4 +128,14 @@ export const ALL_METADATA_RELATED_METADATA_BY_FOREIGN_KEY = { }, rowLevelPermissionPredicate: {}, rowLevelPermissionPredicateGroup: {}, + viewFilterGroup: { + viewId: { + metadataName: 'view', + flatEntityForeignKeyAggregator: 'viewFilterGroupIds', + }, + parentViewFilterGroupId: { + metadataName: 'viewFilterGroup', + flatEntityForeignKeyAggregator: 'childViewFilterGroupIds', + }, + }, } as const satisfies MetadataNameAndRelations; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-metadata-relations-properties.constant.ts b/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-metadata-relations-properties.constant.ts index 7ec83768b9..3b3484bf8e 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-metadata-relations-properties.constant.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-metadata-relations-properties.constant.ts @@ -143,6 +143,14 @@ export const ALL_METADATA_RELATION_PROPERTIES = { rowLevelPermissionPredicates: true, application: true, }, + viewFilterGroup: { + view: true, + viewFilters: true, + parentViewFilterGroup: true, + childViewFilterGroups: true, + workspace: true, + application: true, + }, } as const satisfies { [TName in AllMetadataName]: { [P in ExtractEntityRelatedEntityProperties>]: true; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-metadata-required-metadata-for-validation.constant.ts b/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-metadata-required-metadata-for-validation.constant.ts index c990db7397..bcb6f00ec5 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-metadata-required-metadata-for-validation.constant.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-metadata-required-metadata-for-validation.constant.ts @@ -44,11 +44,16 @@ export const ALL_METADATA_REQUIRED_METADATA_FOR_VALIDATION = { viewFilter: { view: true, fieldMetadata: true, + viewFilterGroup: true, }, viewGroup: { fieldMetadata: true, view: true, }, + viewFilterGroup: { + view: true, + viewFilterGroup: true, + }, role: {}, roleTarget: { role: true, diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.module.ts b/packages/twenty-server/src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.module.ts index d234a5feca..be2fbc436c 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.module.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.module.ts @@ -12,6 +12,7 @@ import { WorkspaceFlatPageLayoutMapCacheService } from 'src/engine/metadata-modu import { WorkspaceFlatRowLevelPermissionPredicateGroupMapCacheService } from 'src/engine/metadata-modules/flat-row-level-permission-predicate/services/workspace-flat-row-level-permission-predicate-group-map-cache.service'; import { WorkspaceFlatRowLevelPermissionPredicateMapCacheService } from 'src/engine/metadata-modules/flat-row-level-permission-predicate/services/workspace-flat-row-level-permission-predicate-map-cache.service'; import { WorkspaceFlatViewFieldMapCacheService } from 'src/engine/metadata-modules/flat-view-field/services/workspace-flat-view-field-map-cache.service'; +import { WorkspaceFlatViewFilterGroupMapCacheService } from 'src/engine/metadata-modules/flat-view-filter-group/services/workspace-flat-view-filter-group-map-cache.service'; import { WorkspaceFlatViewFilterMapCacheService } from 'src/engine/metadata-modules/flat-view-filter/services/workspace-flat-view-filter-map-cache.service'; import { WorkspaceFlatViewGroupMapCacheService } from 'src/engine/metadata-modules/flat-view-group/services/workspace-flat-view-group-map-cache.service'; import { WorkspaceFlatViewMapCacheService } from 'src/engine/metadata-modules/flat-view/services/workspace-flat-view-map-cache.service'; @@ -24,6 +25,7 @@ import { PageLayoutEntity } from 'src/engine/metadata-modules/page-layout/entiti import { RowLevelPermissionPredicateGroupEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate-group.entity'; import { RowLevelPermissionPredicateEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate.entity'; import { ViewFieldEntity } from 'src/engine/metadata-modules/view-field/entities/view-field.entity'; +import { ViewFilterGroupEntity } from 'src/engine/metadata-modules/view-filter-group/entities/view-filter-group.entity'; import { ViewFilterEntity } from 'src/engine/metadata-modules/view-filter/entities/view-filter.entity'; import { ViewGroupEntity } from 'src/engine/metadata-modules/view-group/entities/view-group.entity'; import { ViewEntity } from 'src/engine/metadata-modules/view/entities/view.entity'; @@ -36,6 +38,7 @@ import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache ViewEntity, ViewFieldEntity, ViewFilterEntity, + ViewFilterGroupEntity, ViewGroupEntity, IndexMetadataEntity, IndexFieldMetadataEntity, @@ -54,6 +57,7 @@ import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache WorkspaceFlatViewMapCacheService, WorkspaceFlatViewFieldMapCacheService, WorkspaceFlatViewFilterMapCacheService, + WorkspaceFlatViewFilterGroupMapCacheService, WorkspaceFlatIndexMapCacheService, WorkspaceFlatFieldMetadataMapCacheService, WorkspaceFlatViewGroupMapCacheService, @@ -69,6 +73,7 @@ import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache WorkspaceFlatViewMapCacheService, WorkspaceFlatViewFieldMapCacheService, WorkspaceFlatViewFilterMapCacheService, + WorkspaceFlatViewFilterGroupMapCacheService, WorkspaceFlatIndexMapCacheService, WorkspaceFlatFieldMetadataMapCacheService, WorkspaceFlatViewGroupMapCacheService, diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-entity/types/all-flat-entity-types-by-metadata-name.ts b/packages/twenty-server/src/engine/metadata-modules/flat-entity/types/all-flat-entity-types-by-metadata-name.ts index 6bb4d78ebb..4bebba807d 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-entity/types/all-flat-entity-types-by-metadata-name.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-entity/types/all-flat-entity-types-by-metadata-name.ts @@ -14,6 +14,7 @@ import { type FlatPageLayout } from 'src/engine/metadata-modules/flat-page-layou import { type FlatRoleTarget } from 'src/engine/metadata-modules/flat-role-target/types/flat-role-target.type'; import { type FlatRole } from 'src/engine/metadata-modules/flat-role/types/flat-role.type'; import { type FlatViewField } from 'src/engine/metadata-modules/flat-view-field/types/flat-view-field.type'; +import { type FlatViewFilterGroup } from 'src/engine/metadata-modules/flat-view-filter-group/types/flat-view-filter-group.type'; import { type FlatViewFilter } from 'src/engine/metadata-modules/flat-view-filter/types/flat-view-filter.type'; import { type FlatViewGroup } from 'src/engine/metadata-modules/flat-view-group/types/flat-view-group.type'; import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type'; @@ -33,6 +34,7 @@ import { type FlatRowLevelPermissionPredicate } from 'src/engine/metadata-module import { type ServerlessFunctionEntity } from 'src/engine/metadata-modules/serverless-function/serverless-function.entity'; import { type FlatServerlessFunction } from 'src/engine/metadata-modules/serverless-function/types/flat-serverless-function.type'; import { type ViewFieldEntity } from 'src/engine/metadata-modules/view-field/entities/view-field.entity'; +import { type ViewFilterGroupEntity } from 'src/engine/metadata-modules/view-filter-group/entities/view-filter-group.entity'; import { type ViewFilterEntity } from 'src/engine/metadata-modules/view-filter/entities/view-filter.entity'; import { type ViewGroupEntity } from 'src/engine/metadata-modules/view-group/entities/view-group.entity'; import { type ViewEntity } from 'src/engine/metadata-modules/view/entities/view.entity'; @@ -116,6 +118,11 @@ import { type DeleteViewFieldAction, type UpdateViewFieldAction, } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view-field/types/workspace-migration-view-field-action-v2.type'; +import { + type CreateViewFilterGroupAction, + type DeleteViewFilterGroupAction, + type UpdateViewFilterGroupAction, +} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view-filter-group/types/workspace-migration-view-filter-group-action-v2.type'; import { type CreateViewFilterAction, type DeleteViewFilterAction, @@ -198,6 +205,15 @@ export type AllFlatEntityTypesByMetadataName = { flatEntity: FlatRowLevelPermissionPredicateGroup; entity: RowLevelPermissionPredicateGroupEntity; }; + viewFilterGroup: { + actions: { + created: CreateViewFilterGroupAction; + updated: UpdateViewFilterGroupAction; + deleted: DeleteViewFilterGroupAction; + }; + flatEntity: FlatViewFilterGroup; + entity: ViewFilterGroupEntity; + }; index: { actions: { created: CreateIndexAction; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-view-filter-group/constants/flat-view-filter-group-editable-properties.constant.ts b/packages/twenty-server/src/engine/metadata-modules/flat-view-filter-group/constants/flat-view-filter-group-editable-properties.constant.ts new file mode 100644 index 0000000000..f0f2ff82ee --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-view-filter-group/constants/flat-view-filter-group-editable-properties.constant.ts @@ -0,0 +1,7 @@ +import { type FlatViewFilterGroup } from 'src/engine/metadata-modules/flat-view-filter-group/types/flat-view-filter-group.type'; + +export const FLAT_VIEW_FILTER_GROUP_EDITABLE_PROPERTIES = [ + 'parentViewFilterGroupId', + 'logicalOperator', + 'positionInViewFilterGroup', +] as const satisfies (keyof FlatViewFilterGroup)[]; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-view-filter-group/services/workspace-flat-view-filter-group-map-cache.service.ts b/packages/twenty-server/src/engine/metadata-modules/flat-view-filter-group/services/workspace-flat-view-filter-group-map-cache.service.ts new file mode 100644 index 0000000000..6c0ccda72e --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-view-filter-group/services/workspace-flat-view-filter-group-map-cache.service.ts @@ -0,0 +1,75 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; + +import { type Repository } from 'typeorm'; + +import { WorkspaceCacheProvider } from 'src/engine/workspace-cache/interfaces/workspace-cache-provider.service'; + +import { createEmptyFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/constant/create-empty-flat-entity-maps.constant'; +import { type FlatViewFilterGroupMaps } from 'src/engine/metadata-modules/flat-view-filter-group/types/flat-view-filter-group-maps.type'; +import { fromViewFilterGroupEntityToFlatViewFilterGroup } from 'src/engine/metadata-modules/flat-view-filter-group/utils/from-view-filter-group-entity-to-flat-view-filter-group.util'; +import { ViewFilterGroupEntity } from 'src/engine/metadata-modules/view-filter-group/entities/view-filter-group.entity'; +import { ViewFilterEntity } from 'src/engine/metadata-modules/view-filter/entities/view-filter.entity'; +import { WorkspaceCache } from 'src/engine/workspace-cache/decorators/workspace-cache.decorator'; +import { regroupEntitiesByRelatedEntityId } from 'src/engine/workspace-cache/utils/regroup-entities-by-related-entity-id'; +import { addFlatEntityToFlatEntityMapsThroughMutationOrThrow } from 'src/engine/workspace-manager/workspace-migration-v2/utils/add-flat-entity-to-flat-entity-maps-through-mutation-or-throw.util'; + +@Injectable() +@WorkspaceCache('flatViewFilterGroupMaps') +export class WorkspaceFlatViewFilterGroupMapCacheService extends WorkspaceCacheProvider { + constructor( + @InjectRepository(ViewFilterGroupEntity) + private readonly viewFilterGroupRepository: Repository, + @InjectRepository(ViewFilterEntity) + private readonly viewFilterRepository: Repository, + ) { + super(); + } + + async computeForCache(workspaceId: string): Promise { + const [viewFilterGroups, viewFilters] = await Promise.all([ + this.viewFilterGroupRepository.find({ + where: { workspaceId }, + withDeleted: true, + }), + this.viewFilterRepository.find({ + where: { workspaceId }, + select: ['id', 'viewFilterGroupId'], + withDeleted: true, + }), + ]); + + const [viewFiltersByViewFilterGroupId, childViewFilterGroupsByParentId] = ( + [ + { + entities: viewFilters, + foreignKey: 'viewFilterGroupId', + }, + { + entities: viewFilterGroups, + foreignKey: 'parentViewFilterGroupId', + }, + ] as const + ).map(regroupEntitiesByRelatedEntityId); + + const flatViewFilterGroupMaps = createEmptyFlatEntityMaps(); + + for (const viewFilterGroupEntity of viewFilterGroups) { + const flatViewFilterGroup = + fromViewFilterGroupEntityToFlatViewFilterGroup({ + ...viewFilterGroupEntity, + viewFilters: + viewFiltersByViewFilterGroupId.get(viewFilterGroupEntity.id) || [], + childViewFilterGroups: + childViewFilterGroupsByParentId.get(viewFilterGroupEntity.id) || [], + } as ViewFilterGroupEntity); + + addFlatEntityToFlatEntityMapsThroughMutationOrThrow({ + flatEntity: flatViewFilterGroup, + flatEntityMapsToMutate: flatViewFilterGroupMaps, + }); + } + + return flatViewFilterGroupMaps; + } +} diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-view-filter-group/types/flat-view-filter-group-maps.type.ts b/packages/twenty-server/src/engine/metadata-modules/flat-view-filter-group/types/flat-view-filter-group-maps.type.ts new file mode 100644 index 0000000000..ba86a4efa0 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-view-filter-group/types/flat-view-filter-group-maps.type.ts @@ -0,0 +1,4 @@ +import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type'; +import { type FlatViewFilterGroup } from 'src/engine/metadata-modules/flat-view-filter-group/types/flat-view-filter-group.type'; + +export type FlatViewFilterGroupMaps = FlatEntityMaps; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-view-filter-group/types/flat-view-filter-group.type.ts b/packages/twenty-server/src/engine/metadata-modules/flat-view-filter-group/types/flat-view-filter-group.type.ts new file mode 100644 index 0000000000..e7b4035970 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-view-filter-group/types/flat-view-filter-group.type.ts @@ -0,0 +1,4 @@ +import { type FlatEntityFrom } from 'src/engine/metadata-modules/flat-entity/types/flat-entity.type'; +import { type ViewFilterGroupEntity } from 'src/engine/metadata-modules/view-filter-group/entities/view-filter-group.entity'; + +export type FlatViewFilterGroup = FlatEntityFrom; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-view-filter-group/utils/from-create-view-filter-group-input-to-flat-view-filter-group-to-create.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-view-filter-group/utils/from-create-view-filter-group-input-to-flat-view-filter-group-to-create.util.ts new file mode 100644 index 0000000000..9b3dfb4c4c --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-view-filter-group/utils/from-create-view-filter-group-input-to-flat-view-filter-group-to-create.util.ts @@ -0,0 +1,45 @@ +import { trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties } from 'twenty-shared/utils'; +import { v4 } from 'uuid'; + +import { type FlatViewFilterGroup } from 'src/engine/metadata-modules/flat-view-filter-group/types/flat-view-filter-group.type'; +import { type CreateViewFilterGroupInput } from 'src/engine/metadata-modules/view-filter-group/dtos/inputs/create-view-filter-group.input'; +import { ViewFilterGroupLogicalOperator } from 'src/engine/metadata-modules/view-filter-group/enums/view-filter-group-logical-operator'; + +export const fromCreateViewFilterGroupInputToFlatViewFilterGroupToCreate = ({ + createViewFilterGroupInput: rawCreateViewFilterGroupInput, + workspaceId, + workspaceCustomApplicationId, +}: { + createViewFilterGroupInput: CreateViewFilterGroupInput; + workspaceId: string; + workspaceCustomApplicationId: string; +}): FlatViewFilterGroup => { + const { viewId, ...createViewFilterGroupInput } = + trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties( + rawCreateViewFilterGroupInput, + ['id', 'viewId', 'parentViewFilterGroupId'], + ); + + const createdAt = new Date().toISOString(); + const viewFilterGroupId = createViewFilterGroupInput.id ?? v4(); + + return { + id: viewFilterGroupId, + viewId, + workspaceId, + createdAt, + updatedAt: createdAt, + deletedAt: null, + universalIdentifier: viewFilterGroupId, + logicalOperator: + createViewFilterGroupInput.logicalOperator ?? + ViewFilterGroupLogicalOperator.AND, + parentViewFilterGroupId: + createViewFilterGroupInput.parentViewFilterGroupId ?? null, + positionInViewFilterGroup: + createViewFilterGroupInput.positionInViewFilterGroup ?? null, + applicationId: workspaceCustomApplicationId, + viewFilterIds: [], + childViewFilterGroupIds: [], + }; +}; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-view-filter-group/utils/from-delete-view-filter-group-input-to-flat-view-filter-group-or-throw.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-view-filter-group/utils/from-delete-view-filter-group-input-to-flat-view-filter-group-or-throw.util.ts new file mode 100644 index 0000000000..6152bfd758 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-view-filter-group/utils/from-delete-view-filter-group-input-to-flat-view-filter-group-or-throw.util.ts @@ -0,0 +1,40 @@ +import { + extractAndSanitizeObjectStringFields, + isDefined, +} from 'twenty-shared/utils'; + +import { type FlatViewFilterGroupMaps } from 'src/engine/metadata-modules/flat-view-filter-group/types/flat-view-filter-group-maps.type'; +import { type FlatViewFilterGroup } from 'src/engine/metadata-modules/flat-view-filter-group/types/flat-view-filter-group.type'; +import { type DeleteViewFilterGroupInput } from 'src/engine/metadata-modules/view-filter-group/dtos/inputs/delete-view-filter-group.input'; +import { + ViewFilterGroupException, + ViewFilterGroupExceptionCode, +} from 'src/engine/metadata-modules/view-filter-group/exceptions/view-filter-group.exception'; + +export const fromDeleteViewFilterGroupInputToFlatViewFilterGroupOrThrow = ({ + deleteViewFilterGroupInput: rawDeleteViewFilterGroupInput, + flatViewFilterGroupMaps, +}: { + deleteViewFilterGroupInput: DeleteViewFilterGroupInput; + flatViewFilterGroupMaps: FlatViewFilterGroupMaps; +}): FlatViewFilterGroup => { + const { id: viewFilterGroupId } = extractAndSanitizeObjectStringFields( + rawDeleteViewFilterGroupInput, + ['id'], + ); + + const existingFlatViewFilterGroupToDelete = + flatViewFilterGroupMaps.byId[viewFilterGroupId]; + + if (!isDefined(existingFlatViewFilterGroupToDelete)) { + throw new ViewFilterGroupException( + 'View filter group not found', + ViewFilterGroupExceptionCode.VIEW_FILTER_GROUP_NOT_FOUND, + ); + } + + return { + ...existingFlatViewFilterGroupToDelete, + deletedAt: new Date().toISOString(), + }; +}; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-view-filter-group/utils/from-destroy-view-filter-group-input-to-flat-view-filter-group-or-throw.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-view-filter-group/utils/from-destroy-view-filter-group-input-to-flat-view-filter-group-or-throw.util.ts new file mode 100644 index 0000000000..a4b1b64781 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-view-filter-group/utils/from-destroy-view-filter-group-input-to-flat-view-filter-group-or-throw.util.ts @@ -0,0 +1,37 @@ +import { + extractAndSanitizeObjectStringFields, + isDefined, +} from 'twenty-shared/utils'; + +import { type FlatViewFilterGroupMaps } from 'src/engine/metadata-modules/flat-view-filter-group/types/flat-view-filter-group-maps.type'; +import { type FlatViewFilterGroup } from 'src/engine/metadata-modules/flat-view-filter-group/types/flat-view-filter-group.type'; +import { type DestroyViewFilterGroupInput } from 'src/engine/metadata-modules/view-filter-group/dtos/inputs/destroy-view-filter-group.input'; +import { + ViewFilterGroupException, + ViewFilterGroupExceptionCode, +} from 'src/engine/metadata-modules/view-filter-group/exceptions/view-filter-group.exception'; + +export const fromDestroyViewFilterGroupInputToFlatViewFilterGroupOrThrow = ({ + destroyViewFilterGroupInput, + flatViewFilterGroupMaps, +}: { + destroyViewFilterGroupInput: DestroyViewFilterGroupInput; + flatViewFilterGroupMaps: FlatViewFilterGroupMaps; +}): FlatViewFilterGroup => { + const { id: viewFilterGroupId } = extractAndSanitizeObjectStringFields( + destroyViewFilterGroupInput, + ['id'], + ); + + const existingFlatViewFilterGroupToDestroy = + flatViewFilterGroupMaps.byId[viewFilterGroupId]; + + if (!isDefined(existingFlatViewFilterGroupToDestroy)) { + throw new ViewFilterGroupException( + 'View filter group not found', + ViewFilterGroupExceptionCode.VIEW_FILTER_GROUP_NOT_FOUND, + ); + } + + return existingFlatViewFilterGroupToDestroy; +}; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-view-filter-group/utils/from-update-view-filter-group-input-to-flat-view-filter-group-to-update-or-throw.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-view-filter-group/utils/from-update-view-filter-group-input-to-flat-view-filter-group-to-update-or-throw.util.ts new file mode 100644 index 0000000000..93a7dff9e9 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-view-filter-group/utils/from-update-view-filter-group-input-to-flat-view-filter-group-to-update-or-throw.util.ts @@ -0,0 +1,51 @@ +import { + extractAndSanitizeObjectStringFields, + isDefined, + trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties, +} from 'twenty-shared/utils'; + +import { FLAT_VIEW_FILTER_GROUP_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-view-filter-group/constants/flat-view-filter-group-editable-properties.constant'; +import { type FlatViewFilterGroupMaps } from 'src/engine/metadata-modules/flat-view-filter-group/types/flat-view-filter-group-maps.type'; +import { type FlatViewFilterGroup } from 'src/engine/metadata-modules/flat-view-filter-group/types/flat-view-filter-group.type'; +import { type UpdateViewFilterGroupInput } from 'src/engine/metadata-modules/view-filter-group/dtos/inputs/update-view-filter-group.input'; +import { + ViewFilterGroupException, + ViewFilterGroupExceptionCode, +} from 'src/engine/metadata-modules/view-filter-group/exceptions/view-filter-group.exception'; +import { mergeUpdateInExistingRecord } from 'src/utils/merge-update-in-existing-record.util'; + +export const fromUpdateViewFilterGroupInputToFlatViewFilterGroupToUpdateOrThrow = + ({ + updateViewFilterGroupInput: rawUpdateViewFilterGroupInput, + flatViewFilterGroupMaps, + }: { + updateViewFilterGroupInput: UpdateViewFilterGroupInput & { id: string }; + flatViewFilterGroupMaps: FlatViewFilterGroupMaps; + }): FlatViewFilterGroup => { + const { id: viewFilterGroupToUpdateId } = + trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties( + rawUpdateViewFilterGroupInput, + ['id'], + ); + + const existingFlatViewFilterGroupToUpdate = + flatViewFilterGroupMaps.byId[viewFilterGroupToUpdateId]; + + if (!isDefined(existingFlatViewFilterGroupToUpdate)) { + throw new ViewFilterGroupException( + 'View filter group not found', + ViewFilterGroupExceptionCode.VIEW_FILTER_GROUP_NOT_FOUND, + ); + } + + const updatedEditableFieldProperties = extractAndSanitizeObjectStringFields( + rawUpdateViewFilterGroupInput, + FLAT_VIEW_FILTER_GROUP_EDITABLE_PROPERTIES, + ); + + return mergeUpdateInExistingRecord({ + existing: existingFlatViewFilterGroupToUpdate, + properties: FLAT_VIEW_FILTER_GROUP_EDITABLE_PROPERTIES, + update: updatedEditableFieldProperties, + }); + }; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-view-filter-group/utils/from-view-filter-group-entity-to-flat-view-filter-group.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-view-filter-group/utils/from-view-filter-group-entity-to-flat-view-filter-group.util.ts new file mode 100644 index 0000000000..dee5c6fbfc --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-view-filter-group/utils/from-view-filter-group-entity-to-flat-view-filter-group.util.ts @@ -0,0 +1,33 @@ +import { removePropertiesFromRecord } from 'twenty-shared/utils'; + +import { ALL_METADATA_RELATION_PROPERTIES } from 'src/engine/metadata-modules/flat-entity/constant/all-metadata-relations-properties.constant'; +import { type FlatViewFilterGroup } from 'src/engine/metadata-modules/flat-view-filter-group/types/flat-view-filter-group.type'; +import { type ViewFilterGroupEntity } from 'src/engine/metadata-modules/view-filter-group/entities/view-filter-group.entity'; + +export const fromViewFilterGroupEntityToFlatViewFilterGroup = ( + viewFilterGroupEntity: ViewFilterGroupEntity, +): FlatViewFilterGroup => { + const viewFilterGroupEntityWithoutRelations = removePropertiesFromRecord( + viewFilterGroupEntity, + Object.keys( + ALL_METADATA_RELATION_PROPERTIES.viewFilterGroup, + ) as (keyof typeof ALL_METADATA_RELATION_PROPERTIES.viewFilterGroup)[], + ); + + return { + ...viewFilterGroupEntityWithoutRelations, + createdAt: viewFilterGroupEntity.createdAt.toISOString(), + updatedAt: viewFilterGroupEntity.updatedAt.toISOString(), + deletedAt: viewFilterGroupEntity.deletedAt?.toISOString() ?? null, + universalIdentifier: + viewFilterGroupEntityWithoutRelations.universalIdentifier ?? + viewFilterGroupEntityWithoutRelations.id, + viewFilterIds: + viewFilterGroupEntity.viewFilters?.map((viewFilter) => viewFilter.id) ?? + [], + childViewFilterGroupIds: + viewFilterGroupEntity.childViewFilterGroups?.map( + (childGroup) => childGroup.id, + ) ?? [], + }; +}; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-view-filter/types/flat-view-filter.type.ts b/packages/twenty-server/src/engine/metadata-modules/flat-view-filter/types/flat-view-filter.type.ts index f96f24f165..32798529bc 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-view-filter/types/flat-view-filter.type.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-view-filter/types/flat-view-filter.type.ts @@ -1,7 +1,4 @@ import { type FlatEntityFrom } from 'src/engine/metadata-modules/flat-entity/types/flat-entity.type'; import { type ViewFilterEntity } from 'src/engine/metadata-modules/view-filter/entities/view-filter.entity'; -export type FlatViewFilter = FlatEntityFrom< - // TODO remove once viewFilterGroups have been migrated to v2 - Omit ->; +export type FlatViewFilter = FlatEntityFrom; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-view/flat-view.module.ts b/packages/twenty-server/src/engine/metadata-modules/flat-view/flat-view.module.ts index e3d728c121..fc64d07689 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-view/flat-view.module.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-view/flat-view.module.ts @@ -3,6 +3,7 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { WorkspaceFlatViewMapCacheService } from 'src/engine/metadata-modules/flat-view/services/workspace-flat-view-map-cache.service'; import { ViewFieldEntity } from 'src/engine/metadata-modules/view-field/entities/view-field.entity'; +import { ViewFilterGroupEntity } from 'src/engine/metadata-modules/view-filter-group/entities/view-filter-group.entity'; import { ViewFilterEntity } from 'src/engine/metadata-modules/view-filter/entities/view-filter.entity'; import { ViewGroupEntity } from 'src/engine/metadata-modules/view-group/entities/view-group.entity'; import { ViewEntity } from 'src/engine/metadata-modules/view/entities/view.entity'; @@ -14,6 +15,7 @@ import { ViewEntity } from 'src/engine/metadata-modules/view/entities/view.entit ViewFieldEntity, ViewFilterEntity, ViewGroupEntity, + ViewFilterGroupEntity, ]), ], providers: [WorkspaceFlatViewMapCacheService], diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-view/services/workspace-flat-view-map-cache.service.ts b/packages/twenty-server/src/engine/metadata-modules/flat-view/services/workspace-flat-view-map-cache.service.ts index 5e10cbdcf4..5ee854ffaf 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-view/services/workspace-flat-view-map-cache.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-view/services/workspace-flat-view-map-cache.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; +import { type Repository } from 'typeorm'; import { WorkspaceCacheProvider } from 'src/engine/workspace-cache/interfaces/workspace-cache-provider.service'; @@ -9,6 +9,7 @@ import { createEmptyFlatEntityMaps } from 'src/engine/metadata-modules/flat-enti import { type FlatViewMaps } from 'src/engine/metadata-modules/flat-view/types/flat-view-maps.type'; import { fromViewEntityToFlatView } from 'src/engine/metadata-modules/flat-view/utils/from-view-entity-to-flat-view.util'; import { ViewFieldEntity } from 'src/engine/metadata-modules/view-field/entities/view-field.entity'; +import { ViewFilterGroupEntity } from 'src/engine/metadata-modules/view-filter-group/entities/view-filter-group.entity'; import { ViewFilterEntity } from 'src/engine/metadata-modules/view-filter/entities/view-filter.entity'; import { ViewGroupEntity } from 'src/engine/metadata-modules/view-group/entities/view-group.entity'; import { ViewEntity } from 'src/engine/metadata-modules/view/entities/view.entity'; @@ -28,34 +29,47 @@ export class WorkspaceFlatViewMapCacheService extends WorkspaceCacheProvider, @InjectRepository(ViewGroupEntity) private readonly viewGroupRepository: Repository, + @InjectRepository(ViewFilterGroupEntity) + private readonly viewFilterGroupRepository: Repository, ) { super(); } async computeForCache(workspaceId: string): Promise { - const [views, viewFields, viewFilters, viewGroups] = await Promise.all([ - this.viewRepository.find({ - where: { workspaceId }, - withDeleted: true, - }), - this.viewFieldRepository.find({ - where: { workspaceId }, - select: ['id', 'viewId'], - withDeleted: true, - }), - this.viewFilterRepository.find({ - where: { workspaceId }, - select: ['id', 'viewId'], - withDeleted: true, - }), - this.viewGroupRepository.find({ - where: { workspaceId }, - select: ['id', 'viewId'], - withDeleted: true, - }), - ]); + const [views, viewFields, viewFilters, viewGroups, viewFilterGroups] = + await Promise.all([ + this.viewRepository.find({ + where: { workspaceId }, + withDeleted: true, + }), + this.viewFieldRepository.find({ + where: { workspaceId }, + select: ['id', 'viewId'], + withDeleted: true, + }), + this.viewFilterRepository.find({ + where: { workspaceId }, + select: ['id', 'viewId'], + withDeleted: true, + }), + this.viewGroupRepository.find({ + where: { workspaceId }, + select: ['id', 'viewId'], + withDeleted: true, + }), + this.viewFilterGroupRepository.find({ + where: { workspaceId }, + select: ['id', 'viewId'], + withDeleted: true, + }), + ]); - const [viewFieldsByViewId, viewFiltersByViewId, viewGroupsByViewId] = ( + const [ + viewFieldsByViewId, + viewFiltersByViewId, + viewGroupsByViewId, + viewFilterGroupsByViewId, + ] = ( [ { entities: viewFields, @@ -69,6 +83,10 @@ export class WorkspaceFlatViewMapCacheService extends WorkspaceCacheProvider + // TODO remove once viewSorts has been migrated to v2 + Omit >; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-view/utils/from-create-view-input-to-flat-view-to-create.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-view/utils/from-create-view-input-to-flat-view-to-create.util.ts index be13dc55c4..fb4455ac09 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-view/utils/from-create-view-input-to-flat-view-to-create.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-view/utils/from-create-view-input-to-flat-view-to-create.util.ts @@ -68,6 +68,7 @@ export const fromCreateViewInputToFlatViewToCreate = ({ viewFieldIds: [], viewFilterIds: [], viewGroupIds: [], + viewFilterGroupIds: [], applicationId: workspaceCustomApplicationId, }; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-view/utils/from-view-entity-to-flat-view.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-view/utils/from-view-entity-to-flat-view.util.ts index af60b4f73a..89497870da 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-view/utils/from-view-entity-to-flat-view.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-view/utils/from-view-entity-to-flat-view.util.ts @@ -23,5 +23,9 @@ export const fromViewEntityToFlatView = (viewEntity: ViewEntity): FlatView => { viewFieldIds: viewEntity.viewFields.map((viewField) => viewField.id), viewFilterIds: viewEntity.viewFilters.map((viewFilter) => viewFilter.id), viewGroupIds: viewEntity.viewGroups.map((viewGroup) => viewGroup.id), + viewFilterGroupIds: + viewEntity.viewFilterGroups?.map( + (viewFilterGroup) => viewFilterGroup.id, + ) ?? [], }; }; diff --git a/packages/twenty-server/src/engine/metadata-modules/view-filter-group/controllers/view-filter-group.controller.ts b/packages/twenty-server/src/engine/metadata-modules/view-filter-group/controllers/view-filter-group.controller.ts index a45185929b..81081de711 100644 --- a/packages/twenty-server/src/engine/metadata-modules/view-filter-group/controllers/view-filter-group.controller.ts +++ b/packages/twenty-server/src/engine/metadata-modules/view-filter-group/controllers/view-filter-group.controller.ts @@ -13,12 +13,12 @@ import { import { isDefined } from 'twenty-shared/utils'; -import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; +import { type WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator'; import { NoPermissionGuard } from 'src/engine/guards/no-permission.guard'; import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; -import { CreateViewFilterGroupInput } from 'src/engine/metadata-modules/view-filter-group/dtos/inputs/create-view-filter-group.input'; -import { UpdateViewFilterGroupInput } from 'src/engine/metadata-modules/view-filter-group/dtos/inputs/update-view-filter-group.input'; +import { type CreateViewFilterGroupInput } from 'src/engine/metadata-modules/view-filter-group/dtos/inputs/create-view-filter-group.input'; +import { type UpdateViewFilterGroupInput } from 'src/engine/metadata-modules/view-filter-group/dtos/inputs/update-view-filter-group.input'; import { type ViewFilterGroupDTO } from 'src/engine/metadata-modules/view-filter-group/dtos/view-filter-group.dto'; import { generateViewFilterGroupExceptionMessage, @@ -90,8 +90,8 @@ export class ViewFilterGroupController { @Body() input: CreateViewFilterGroupInput, @AuthWorkspace() workspace: WorkspaceEntity, ): Promise { - return this.viewFilterGroupService.create({ - ...input, + return this.viewFilterGroupService.createOne({ + createViewFilterGroupInput: input, workspaceId: workspace.id, }); } @@ -103,13 +103,11 @@ export class ViewFilterGroupController { @Body() input: UpdateViewFilterGroupInput, @AuthWorkspace() workspace: WorkspaceEntity, ): Promise { - const updatedViewFilterGroup = await this.viewFilterGroupService.update( + return this.viewFilterGroupService.updateOne({ id, - workspace.id, - input, - ); - - return updatedViewFilterGroup; + updateViewFilterGroupInput: input, + workspaceId: workspace.id, + }); } @Delete(':id') @@ -118,10 +116,10 @@ export class ViewFilterGroupController { @Param('id') id: string, @AuthWorkspace() workspace: WorkspaceEntity, ): Promise<{ success: boolean }> { - const deletedViewFilterGroup = await this.viewFilterGroupService.delete( - id, - workspace.id, - ); + const deletedViewFilterGroup = await this.viewFilterGroupService.deleteOne({ + deleteViewFilterGroupInput: { id }, + workspaceId: workspace.id, + }); return { success: isDefined(deletedViewFilterGroup) }; } diff --git a/packages/twenty-server/src/engine/metadata-modules/view-filter-group/dtos/inputs/create-view-filter-group.input.ts b/packages/twenty-server/src/engine/metadata-modules/view-filter-group/dtos/inputs/create-view-filter-group.input.ts index ba1aa51781..6942803df3 100644 --- a/packages/twenty-server/src/engine/metadata-modules/view-filter-group/dtos/inputs/create-view-filter-group.input.ts +++ b/packages/twenty-server/src/engine/metadata-modules/view-filter-group/dtos/inputs/create-view-filter-group.input.ts @@ -13,7 +13,7 @@ export class CreateViewFilterGroupInput { @Field(() => ViewFilterGroupLogicalOperator, { nullable: true, - defaultValue: ViewFilterGroupLogicalOperator.NOT, + defaultValue: ViewFilterGroupLogicalOperator.AND, }) logicalOperator?: ViewFilterGroupLogicalOperator; diff --git a/packages/twenty-server/src/engine/metadata-modules/view-filter-group/dtos/inputs/delete-view-filter-group.input.ts b/packages/twenty-server/src/engine/metadata-modules/view-filter-group/dtos/inputs/delete-view-filter-group.input.ts new file mode 100644 index 0000000000..8845765e29 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/view-filter-group/dtos/inputs/delete-view-filter-group.input.ts @@ -0,0 +1,15 @@ +import { InputType } from '@nestjs/graphql'; + +import { IDField } from '@ptc-org/nestjs-query-graphql'; +import { IsUUID } from 'class-validator'; + +import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars'; + +@InputType() +export class DeleteViewFilterGroupInput { + @IDField(() => UUIDScalarType, { + description: 'The id of the view filter group to delete.', + }) + @IsUUID() + id: string; +} diff --git a/packages/twenty-server/src/engine/metadata-modules/view-filter-group/dtos/inputs/destroy-view-filter-group.input.ts b/packages/twenty-server/src/engine/metadata-modules/view-filter-group/dtos/inputs/destroy-view-filter-group.input.ts new file mode 100644 index 0000000000..63b5b0c69a --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/view-filter-group/dtos/inputs/destroy-view-filter-group.input.ts @@ -0,0 +1,15 @@ +import { InputType } from '@nestjs/graphql'; + +import { IDField } from '@ptc-org/nestjs-query-graphql'; +import { IsUUID } from 'class-validator'; + +import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars'; + +@InputType() +export class DestroyViewFilterGroupInput { + @IDField(() => UUIDScalarType, { + description: 'The id of the view filter group to destroy.', + }) + @IsUUID() + id: string; +} diff --git a/packages/twenty-server/src/engine/metadata-modules/view-filter-group/exceptions/view-filter-group.exception.ts b/packages/twenty-server/src/engine/metadata-modules/view-filter-group/exceptions/view-filter-group.exception.ts index 1238924fcb..777379b197 100644 --- a/packages/twenty-server/src/engine/metadata-modules/view-filter-group/exceptions/view-filter-group.exception.ts +++ b/packages/twenty-server/src/engine/metadata-modules/view-filter-group/exceptions/view-filter-group.exception.ts @@ -21,6 +21,8 @@ export enum ViewFilterGroupExceptionCode { VIEW_FILTER_GROUP_NOT_FOUND = 'VIEW_FILTER_GROUP_NOT_FOUND', INVALID_VIEW_FILTER_GROUP_DATA = 'INVALID_VIEW_FILTER_GROUP_DATA', VIEW_NOT_FOUND = 'VIEW_NOT_FOUND', + CIRCULAR_DEPENDENCY = 'CIRCULAR_DEPENDENCY', + MAX_DEPTH_EXCEEDED = 'MAX_DEPTH_EXCEEDED', } export enum ViewFilterGroupExceptionMessageKey { diff --git a/packages/twenty-server/src/engine/metadata-modules/view-filter-group/resolvers/view-filter-group.resolver.ts b/packages/twenty-server/src/engine/metadata-modules/view-filter-group/resolvers/view-filter-group.resolver.ts index 808b0b713c..60726b9c7a 100644 --- a/packages/twenty-server/src/engine/metadata-modules/view-filter-group/resolvers/view-filter-group.resolver.ts +++ b/packages/twenty-server/src/engine/metadata-modules/view-filter-group/resolvers/view-filter-group.resolver.ts @@ -3,7 +3,7 @@ import { Args, Mutation, Query, Resolver } from '@nestjs/graphql'; import { isDefined } from 'twenty-shared/utils'; -import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; +import { type WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator'; import { NoPermissionGuard } from 'src/engine/guards/no-permission.guard'; import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; @@ -54,8 +54,8 @@ export class ViewFilterGroupResolver { @Args('input') input: CreateViewFilterGroupInput, @AuthWorkspace() workspace: WorkspaceEntity, ): Promise { - return this.viewFilterGroupService.create({ - ...input, + return this.viewFilterGroupService.createOne({ + createViewFilterGroupInput: input, workspaceId: workspace.id, }); } @@ -67,7 +67,11 @@ export class ViewFilterGroupResolver { @Args('input') input: UpdateViewFilterGroupInput, @AuthWorkspace() workspace: WorkspaceEntity, ): Promise { - return this.viewFilterGroupService.update(id, workspace.id, input); + return this.viewFilterGroupService.updateOne({ + id, + updateViewFilterGroupInput: input, + workspaceId: workspace.id, + }); } @Mutation(() => Boolean) @@ -76,10 +80,10 @@ export class ViewFilterGroupResolver { @Args('id', { type: () => String }) id: string, @AuthWorkspace() workspace: WorkspaceEntity, ): Promise { - const deletedViewFilterGroup = await this.viewFilterGroupService.delete( - id, - workspace.id, - ); + const deletedViewFilterGroup = await this.viewFilterGroupService.deleteOne({ + deleteViewFilterGroupInput: { id }, + workspaceId: workspace.id, + }); return isDefined(deletedViewFilterGroup); } @@ -90,6 +94,12 @@ export class ViewFilterGroupResolver { @Args('id', { type: () => String }) id: string, @AuthWorkspace() workspace: WorkspaceEntity, ): Promise { - return this.viewFilterGroupService.destroy(id, workspace.id); + const destroyedViewFilterGroup = + await this.viewFilterGroupService.destroyOne({ + destroyViewFilterGroupInput: { id }, + workspaceId: workspace.id, + }); + + return isDefined(destroyedViewFilterGroup); } } diff --git a/packages/twenty-server/src/engine/metadata-modules/view-filter-group/services/tests/view-filter-group.service.spec.ts b/packages/twenty-server/src/engine/metadata-modules/view-filter-group/services/tests/view-filter-group.service.spec.ts index 632418558d..f018db309e 100644 --- a/packages/twenty-server/src/engine/metadata-modules/view-filter-group/services/tests/view-filter-group.service.spec.ts +++ b/packages/twenty-server/src/engine/metadata-modules/view-filter-group/services/tests/view-filter-group.service.spec.ts @@ -3,17 +3,12 @@ import { getRepositoryToken } from '@nestjs/typeorm'; import { type Repository } from 'typeorm'; +import { ApplicationService } from 'src/engine/core-modules/application/application.service'; +import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service'; import { ViewFilterGroupEntity } from 'src/engine/metadata-modules/view-filter-group/entities/view-filter-group.entity'; import { ViewFilterGroupLogicalOperator } from 'src/engine/metadata-modules/view-filter-group/enums/view-filter-group-logical-operator'; -import { - ViewFilterGroupException, - ViewFilterGroupExceptionCode, - ViewFilterGroupExceptionMessageKey, - generateViewFilterGroupExceptionMessage, - generateViewFilterGroupUserFriendlyExceptionMessage, -} from 'src/engine/metadata-modules/view-filter-group/exceptions/view-filter-group.exception'; import { ViewFilterGroupService } from 'src/engine/metadata-modules/view-filter-group/services/view-filter-group.service'; -import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service'; +import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration-v2/services/workspace-migration-validate-build-and-run-service'; describe('ViewFilterGroupService', () => { let viewFilterGroupService: ViewFilterGroupService; @@ -46,9 +41,22 @@ describe('ViewFilterGroupService', () => { }, }, { - provide: WorkspaceCacheStorageService, + provide: WorkspaceMigrationValidateBuildAndRunService, useValue: { - flushGraphQLOperation: jest.fn(), + validateBuildAndRunWorkspaceMigration: jest.fn(), + }, + }, + { + provide: WorkspaceManyOrAllFlatEntityMapsCacheService, + useValue: { + getOrRecomputeManyOrAllFlatEntityMaps: jest.fn(), + invalidateFlatEntityMaps: jest.fn(), + }, + }, + { + provide: ApplicationService, + useValue: { + findWorkspaceTwentyStandardAndCustomApplicationOrThrow: jest.fn(), }, }, ], @@ -169,202 +177,4 @@ describe('ViewFilterGroupService', () => { expect(result).toBeNull(); }); }); - - describe('create', () => { - const validViewFilterGroupData = { - viewId: 'view-id', - workspaceId: 'workspace-id', - logicalOperator: ViewFilterGroupLogicalOperator.AND, - positionInViewFilterGroup: 0, - }; - - it('should create a view filter group successfully', async () => { - jest - .spyOn(viewFilterGroupRepository, 'create') - .mockReturnValue(mockViewFilterGroup); - jest - .spyOn(viewFilterGroupRepository, 'save') - .mockResolvedValue(mockViewFilterGroup); - - const result = await viewFilterGroupService.create( - validViewFilterGroupData, - ); - - expect(viewFilterGroupRepository.create).toHaveBeenCalledWith( - validViewFilterGroupData, - ); - expect(viewFilterGroupRepository.save).toHaveBeenCalledWith( - mockViewFilterGroup, - ); - expect(result).toEqual(mockViewFilterGroup); - }); - - it('should throw exception when workspaceId is missing', async () => { - const invalidData = { - ...validViewFilterGroupData, - workspaceId: undefined, - }; - - await expect(viewFilterGroupService.create(invalidData)).rejects.toThrow( - new ViewFilterGroupException( - generateViewFilterGroupExceptionMessage( - ViewFilterGroupExceptionMessageKey.WORKSPACE_ID_REQUIRED, - ), - ViewFilterGroupExceptionCode.INVALID_VIEW_FILTER_GROUP_DATA, - { - userFriendlyMessage: - generateViewFilterGroupUserFriendlyExceptionMessage( - ViewFilterGroupExceptionMessageKey.WORKSPACE_ID_REQUIRED, - ), - }, - ), - ); - }); - - it('should throw exception when viewId is missing', async () => { - const invalidData = { ...validViewFilterGroupData, viewId: undefined }; - - await expect(viewFilterGroupService.create(invalidData)).rejects.toThrow( - new ViewFilterGroupException( - generateViewFilterGroupExceptionMessage( - ViewFilterGroupExceptionMessageKey.VIEW_ID_REQUIRED, - ), - ViewFilterGroupExceptionCode.INVALID_VIEW_FILTER_GROUP_DATA, - { - userFriendlyMessage: - generateViewFilterGroupUserFriendlyExceptionMessage( - ViewFilterGroupExceptionMessageKey.VIEW_ID_REQUIRED, - ), - }, - ), - ); - }); - }); - - describe('update', () => { - it('should update a view filter group successfully', async () => { - const id = 'view-filter-group-id'; - const workspaceId = 'workspace-id'; - const updateData = { logicalOperator: ViewFilterGroupLogicalOperator.OR }; - const updatedViewFilterGroup = { ...mockViewFilterGroup, ...updateData }; - - jest - .spyOn(viewFilterGroupService, 'findById') - .mockResolvedValue(mockViewFilterGroup); - jest - .spyOn(viewFilterGroupRepository, 'save') - .mockResolvedValue(updatedViewFilterGroup); - - const result = await viewFilterGroupService.update( - id, - workspaceId, - updateData, - ); - - expect(viewFilterGroupService.findById).toHaveBeenCalledWith( - id, - workspaceId, - ); - expect(viewFilterGroupRepository.save).toHaveBeenCalledWith({ - id, - ...updateData, - }); - expect(result).toEqual({ - ...mockViewFilterGroup, - ...updatedViewFilterGroup, - }); - }); - - it('should throw exception when view filter group is not found', async () => { - const id = 'non-existent-id'; - const workspaceId = 'workspace-id'; - const updateData = { logicalOperator: ViewFilterGroupLogicalOperator.OR }; - - jest.spyOn(viewFilterGroupService, 'findById').mockResolvedValue(null); - - await expect( - viewFilterGroupService.update(id, workspaceId, updateData), - ).rejects.toThrow( - new ViewFilterGroupException( - generateViewFilterGroupExceptionMessage( - ViewFilterGroupExceptionMessageKey.VIEW_FILTER_GROUP_NOT_FOUND, - id, - ), - ViewFilterGroupExceptionCode.VIEW_FILTER_GROUP_NOT_FOUND, - ), - ); - }); - }); - - describe('delete', () => { - it('should delete a view filter group successfully', async () => { - const id = 'view-filter-group-id'; - const workspaceId = 'workspace-id'; - - jest - .spyOn(viewFilterGroupService, 'findById') - .mockResolvedValue(mockViewFilterGroup); - jest - .spyOn(viewFilterGroupRepository, 'softDelete') - .mockResolvedValue({} as any); - - const result = await viewFilterGroupService.delete(id, workspaceId); - - expect(viewFilterGroupService.findById).toHaveBeenCalledWith( - id, - workspaceId, - ); - expect(viewFilterGroupRepository.softDelete).toHaveBeenCalledWith(id); - expect(result).toEqual(mockViewFilterGroup); - }); - - it('should throw exception when view filter group is not found', async () => { - const id = 'non-existent-id'; - const workspaceId = 'workspace-id'; - - jest.spyOn(viewFilterGroupService, 'findById').mockResolvedValue(null); - - await expect( - viewFilterGroupService.delete(id, workspaceId), - ).rejects.toThrow( - new ViewFilterGroupException( - generateViewFilterGroupExceptionMessage( - ViewFilterGroupExceptionMessageKey.VIEW_FILTER_GROUP_NOT_FOUND, - id, - ), - ViewFilterGroupExceptionCode.VIEW_FILTER_GROUP_NOT_FOUND, - ), - ); - }); - }); - - describe('destroy', () => { - it('should destroy a view filter group successfully', async () => { - const id = 'view-filter-group-id'; - const workspaceId = 'workspace-id'; - - jest - .spyOn(viewFilterGroupRepository, 'findOne') - .mockResolvedValue(mockViewFilterGroup); - jest - .spyOn(viewFilterGroupRepository, 'delete') - .mockResolvedValue({} as any); - - const result = await viewFilterGroupService.destroy(id, workspaceId); - - expect(viewFilterGroupRepository.findOne).toHaveBeenCalledWith({ - where: { id, workspaceId }, - relations: [ - 'workspace', - 'view', - 'viewFilters', - 'parentViewFilterGroup', - 'childViewFilterGroups', - ], - withDeleted: true, - }); - expect(viewFilterGroupRepository.delete).toHaveBeenCalledWith(id); - expect(result).toEqual(true); - }); - }); }); diff --git a/packages/twenty-server/src/engine/metadata-modules/view-filter-group/services/view-filter-group.service.ts b/packages/twenty-server/src/engine/metadata-modules/view-filter-group/services/view-filter-group.service.ts index 2cb423322e..d6be049aa6 100644 --- a/packages/twenty-server/src/engine/metadata-modules/view-filter-group/services/view-filter-group.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/view-filter-group/services/view-filter-group.service.ts @@ -2,27 +2,270 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { isDefined } from 'twenty-shared/utils'; -import { IsNull, Repository } from 'typeorm'; +import { IsNull, type Repository } from 'typeorm'; +import { ApplicationService } from 'src/engine/core-modules/application/application.service'; +import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service'; +import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util'; +import { fromCreateViewFilterGroupInputToFlatViewFilterGroupToCreate } from 'src/engine/metadata-modules/flat-view-filter-group/utils/from-create-view-filter-group-input-to-flat-view-filter-group-to-create.util'; +import { fromDeleteViewFilterGroupInputToFlatViewFilterGroupOrThrow } from 'src/engine/metadata-modules/flat-view-filter-group/utils/from-delete-view-filter-group-input-to-flat-view-filter-group-or-throw.util'; +import { fromDestroyViewFilterGroupInputToFlatViewFilterGroupOrThrow } from 'src/engine/metadata-modules/flat-view-filter-group/utils/from-destroy-view-filter-group-input-to-flat-view-filter-group-or-throw.util'; +import { fromUpdateViewFilterGroupInputToFlatViewFilterGroupToUpdateOrThrow } from 'src/engine/metadata-modules/flat-view-filter-group/utils/from-update-view-filter-group-input-to-flat-view-filter-group-to-update-or-throw.util'; +import { type CreateViewFilterGroupInput } from 'src/engine/metadata-modules/view-filter-group/dtos/inputs/create-view-filter-group.input'; +import { type DeleteViewFilterGroupInput } from 'src/engine/metadata-modules/view-filter-group/dtos/inputs/delete-view-filter-group.input'; +import { type DestroyViewFilterGroupInput } from 'src/engine/metadata-modules/view-filter-group/dtos/inputs/destroy-view-filter-group.input'; +import { type UpdateViewFilterGroupInput } from 'src/engine/metadata-modules/view-filter-group/dtos/inputs/update-view-filter-group.input'; +import { type ViewFilterGroupDTO } from 'src/engine/metadata-modules/view-filter-group/dtos/view-filter-group.dto'; import { ViewFilterGroupEntity } from 'src/engine/metadata-modules/view-filter-group/entities/view-filter-group.entity'; -import { - ViewFilterGroupException, - ViewFilterGroupExceptionCode, - ViewFilterGroupExceptionMessageKey, - generateViewFilterGroupExceptionMessage, - generateViewFilterGroupUserFriendlyExceptionMessage, -} from 'src/engine/metadata-modules/view-filter-group/exceptions/view-filter-group.exception'; -import { FIND_ALL_CORE_VIEWS_GRAPHQL_OPERATION } from 'src/engine/metadata-modules/view/constants/find-all-core-views-graphql-operation.constant'; -import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service'; +import { fromFlatViewFilterGroupToViewFilterGroupDto } from 'src/engine/metadata-modules/view-filter-group/utils/from-flat-view-filter-group-to-view-filter-group-dto.util'; +import { WorkspaceMigrationBuilderExceptionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/exceptions/workspace-migration-builder-exception-v2'; +import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration-v2/services/workspace-migration-validate-build-and-run-service'; @Injectable() export class ViewFilterGroupService { constructor( @InjectRepository(ViewFilterGroupEntity) private readonly viewFilterGroupRepository: Repository, - private readonly workspaceCacheStorageService: WorkspaceCacheStorageService, + private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService, + private readonly flatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService, + private readonly applicationService: ApplicationService, ) {} + async createOne({ + createViewFilterGroupInput, + workspaceId, + }: { + createViewFilterGroupInput: CreateViewFilterGroupInput; + workspaceId: string; + }): Promise { + const { workspaceCustomFlatApplication } = + await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow( + { + workspaceId, + }, + ); + + const flatViewFilterGroupToCreate = + fromCreateViewFilterGroupInputToFlatViewFilterGroupToCreate({ + createViewFilterGroupInput, + workspaceId, + workspaceCustomApplicationId: workspaceCustomFlatApplication.id, + }); + + const buildAndRunResult = + await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration( + { + allFlatEntityOperationByMetadataName: { + viewFilterGroup: { + flatEntityToCreate: [flatViewFilterGroupToCreate], + flatEntityToDelete: [], + flatEntityToUpdate: [], + }, + }, + workspaceId, + isSystemBuild: false, + }, + ); + + if (isDefined(buildAndRunResult)) { + throw new WorkspaceMigrationBuilderExceptionV2( + buildAndRunResult, + 'Multiple validation errors occurred while creating view filter group', + ); + } + + const { + flatViewFilterGroupMaps: recomputedExistingFlatViewFilterGroupMaps, + } = + await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps( + { + workspaceId, + flatMapsKeys: ['flatViewFilterGroupMaps'], + }, + ); + + return fromFlatViewFilterGroupToViewFilterGroupDto( + findFlatEntityByIdInFlatEntityMapsOrThrow({ + flatEntityId: flatViewFilterGroupToCreate.id, + flatEntityMaps: recomputedExistingFlatViewFilterGroupMaps, + }), + ); + } + + async updateOne({ + id, + updateViewFilterGroupInput, + workspaceId, + }: { + id: string; + workspaceId: string; + updateViewFilterGroupInput: UpdateViewFilterGroupInput; + }): Promise { + const { flatViewFilterGroupMaps: existingFlatViewFilterGroupMaps } = + await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps( + { + workspaceId, + flatMapsKeys: ['flatViewFilterGroupMaps'], + }, + ); + + const optimisticallyUpdatedFlatViewFilterGroup = + fromUpdateViewFilterGroupInputToFlatViewFilterGroupToUpdateOrThrow({ + flatViewFilterGroupMaps: existingFlatViewFilterGroupMaps, + updateViewFilterGroupInput: { id, ...updateViewFilterGroupInput }, + }); + + const validateAndBuildResult = + await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration( + { + allFlatEntityOperationByMetadataName: { + viewFilterGroup: { + flatEntityToCreate: [], + flatEntityToDelete: [], + flatEntityToUpdate: [optimisticallyUpdatedFlatViewFilterGroup], + }, + }, + workspaceId, + isSystemBuild: false, + }, + ); + + if (isDefined(validateAndBuildResult)) { + throw new WorkspaceMigrationBuilderExceptionV2( + validateAndBuildResult, + 'Multiple validation errors occurred while updating view filter group', + ); + } + + const { + flatViewFilterGroupMaps: recomputedExistingFlatViewFilterGroupMaps, + } = + await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps( + { + workspaceId, + flatMapsKeys: ['flatViewFilterGroupMaps'], + }, + ); + + return fromFlatViewFilterGroupToViewFilterGroupDto( + findFlatEntityByIdInFlatEntityMapsOrThrow({ + flatEntityId: optimisticallyUpdatedFlatViewFilterGroup.id, + flatEntityMaps: recomputedExistingFlatViewFilterGroupMaps, + }), + ); + } + + async deleteOne({ + deleteViewFilterGroupInput, + workspaceId, + }: { + deleteViewFilterGroupInput: DeleteViewFilterGroupInput; + workspaceId: string; + }): Promise { + const { flatViewFilterGroupMaps: existingFlatViewFilterGroupMaps } = + await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps( + { + workspaceId, + flatMapsKeys: ['flatViewFilterGroupMaps'], + }, + ); + + const optimisticallyUpdatedFlatViewFilterGroupWithDeletedAt = + fromDeleteViewFilterGroupInputToFlatViewFilterGroupOrThrow({ + flatViewFilterGroupMaps: existingFlatViewFilterGroupMaps, + deleteViewFilterGroupInput, + }); + + const validateAndBuildResult = + await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration( + { + allFlatEntityOperationByMetadataName: { + viewFilterGroup: { + flatEntityToCreate: [], + flatEntityToDelete: [], + flatEntityToUpdate: [ + optimisticallyUpdatedFlatViewFilterGroupWithDeletedAt, + ], + }, + }, + workspaceId, + isSystemBuild: false, + }, + ); + + if (isDefined(validateAndBuildResult)) { + throw new WorkspaceMigrationBuilderExceptionV2( + validateAndBuildResult, + 'Multiple validation errors occurred while deleting view filter group', + ); + } + + const { + flatViewFilterGroupMaps: recomputedExistingFlatViewFilterGroupMaps, + } = + await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps( + { + workspaceId, + flatMapsKeys: ['flatViewFilterGroupMaps'], + }, + ); + + return fromFlatViewFilterGroupToViewFilterGroupDto( + findFlatEntityByIdInFlatEntityMapsOrThrow({ + flatEntityId: optimisticallyUpdatedFlatViewFilterGroupWithDeletedAt.id, + flatEntityMaps: recomputedExistingFlatViewFilterGroupMaps, + }), + ); + } + + async destroyOne({ + destroyViewFilterGroupInput, + workspaceId, + }: { + destroyViewFilterGroupInput: DestroyViewFilterGroupInput; + workspaceId: string; + }): Promise { + const { flatViewFilterGroupMaps: existingFlatViewFilterGroupMaps } = + await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps( + { + workspaceId, + flatMapsKeys: ['flatViewFilterGroupMaps'], + }, + ); + + const existingViewFilterGroupToDelete = + fromDestroyViewFilterGroupInputToFlatViewFilterGroupOrThrow({ + destroyViewFilterGroupInput, + flatViewFilterGroupMaps: existingFlatViewFilterGroupMaps, + }); + + const validateAndBuildResult = + await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration( + { + allFlatEntityOperationByMetadataName: { + viewFilterGroup: { + flatEntityToCreate: [], + flatEntityToDelete: [existingViewFilterGroupToDelete], + flatEntityToUpdate: [], + }, + }, + workspaceId, + isSystemBuild: false, + }, + ); + + if (isDefined(validateAndBuildResult)) { + throw new WorkspaceMigrationBuilderExceptionV2( + validateAndBuildResult, + 'Multiple validation errors occurred while destroying view filter group', + ); + } + + return fromFlatViewFilterGroupToViewFilterGroupDto( + existingViewFilterGroupToDelete, + ); + } + async findByWorkspaceId( workspaceId: string, ): Promise { @@ -84,138 +327,4 @@ export class ViewFilterGroupService { return viewFilterGroup || null; } - - async create( - viewFilterGroupData: Partial, - ): Promise { - if (!isDefined(viewFilterGroupData.workspaceId)) { - throw new ViewFilterGroupException( - generateViewFilterGroupExceptionMessage( - ViewFilterGroupExceptionMessageKey.WORKSPACE_ID_REQUIRED, - ), - ViewFilterGroupExceptionCode.INVALID_VIEW_FILTER_GROUP_DATA, - { - userFriendlyMessage: - generateViewFilterGroupUserFriendlyExceptionMessage( - ViewFilterGroupExceptionMessageKey.WORKSPACE_ID_REQUIRED, - ), - }, - ); - } - - if (!isDefined(viewFilterGroupData.viewId)) { - throw new ViewFilterGroupException( - generateViewFilterGroupExceptionMessage( - ViewFilterGroupExceptionMessageKey.VIEW_ID_REQUIRED, - ), - ViewFilterGroupExceptionCode.INVALID_VIEW_FILTER_GROUP_DATA, - { - userFriendlyMessage: - generateViewFilterGroupUserFriendlyExceptionMessage( - ViewFilterGroupExceptionMessageKey.VIEW_ID_REQUIRED, - ), - }, - ); - } - - const viewFilterGroup = - this.viewFilterGroupRepository.create(viewFilterGroupData); - - await this.flushGraphQLCache(viewFilterGroupData.workspaceId); - - const savedViewFilterGroup = - await this.viewFilterGroupRepository.save(viewFilterGroup); - - await this.flushGraphQLCache(viewFilterGroupData.workspaceId); - - return savedViewFilterGroup; - } - - async update( - id: string, - workspaceId: string, - updateData: Partial, - ): Promise { - const existingViewFilterGroup = await this.findById(id, workspaceId); - - if (!isDefined(existingViewFilterGroup)) { - throw new ViewFilterGroupException( - generateViewFilterGroupExceptionMessage( - ViewFilterGroupExceptionMessageKey.VIEW_FILTER_GROUP_NOT_FOUND, - id, - ), - ViewFilterGroupExceptionCode.VIEW_FILTER_GROUP_NOT_FOUND, - ); - } - - const updatedViewFilterGroup = await this.viewFilterGroupRepository.save({ - id, - ...updateData, - }); - - await this.flushGraphQLCache(workspaceId); - - return { ...existingViewFilterGroup, ...updatedViewFilterGroup }; - } - - async delete( - id: string, - workspaceId: string, - ): Promise { - const viewFilterGroup = await this.findById(id, workspaceId); - - if (!isDefined(viewFilterGroup)) { - throw new ViewFilterGroupException( - generateViewFilterGroupExceptionMessage( - ViewFilterGroupExceptionMessageKey.VIEW_FILTER_GROUP_NOT_FOUND, - id, - ), - ViewFilterGroupExceptionCode.VIEW_FILTER_GROUP_NOT_FOUND, - ); - } - - await this.viewFilterGroupRepository.softDelete(id); - - return viewFilterGroup; - } - - async destroy(id: string, workspaceId: string): Promise { - const viewFilterGroup = await this.viewFilterGroupRepository.findOne({ - where: { - id, - workspaceId, - }, - relations: [ - 'workspace', - 'view', - 'viewFilters', - 'parentViewFilterGroup', - 'childViewFilterGroups', - ], - withDeleted: true, - }); - - if (!isDefined(viewFilterGroup)) { - throw new ViewFilterGroupException( - generateViewFilterGroupExceptionMessage( - ViewFilterGroupExceptionMessageKey.VIEW_FILTER_GROUP_NOT_FOUND, - id, - ), - ViewFilterGroupExceptionCode.VIEW_FILTER_GROUP_NOT_FOUND, - ); - } - - await this.viewFilterGroupRepository.delete(id); - - await this.flushGraphQLCache(workspaceId); - - return true; - } - - private async flushGraphQLCache(workspaceId: string): Promise { - await this.workspaceCacheStorageService.flushGraphQLOperation({ - operationName: FIND_ALL_CORE_VIEWS_GRAPHQL_OPERATION, - workspaceId, - }); - } } diff --git a/packages/twenty-server/src/engine/metadata-modules/view-filter-group/utils/from-flat-view-filter-group-to-view-filter-group-dto.util.ts b/packages/twenty-server/src/engine/metadata-modules/view-filter-group/utils/from-flat-view-filter-group-to-view-filter-group-dto.util.ts new file mode 100644 index 0000000000..d3c0907bc2 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/view-filter-group/utils/from-flat-view-filter-group-to-view-filter-group-dto.util.ts @@ -0,0 +1,15 @@ +import { type FlatViewFilterGroup } from 'src/engine/metadata-modules/flat-view-filter-group/types/flat-view-filter-group.type'; +import { type ViewFilterGroupDTO } from 'src/engine/metadata-modules/view-filter-group/dtos/view-filter-group.dto'; + +export const fromFlatViewFilterGroupToViewFilterGroupDto = ( + flatViewFilterGroup: FlatViewFilterGroup, +): ViewFilterGroupDTO => { + const { createdAt, updatedAt, deletedAt, ...rest } = flatViewFilterGroup; + + return { + ...rest, + createdAt: new Date(createdAt), + updatedAt: new Date(updatedAt), + deletedAt: deletedAt ? new Date(deletedAt) : null, + }; +}; diff --git a/packages/twenty-server/src/engine/metadata-modules/view-filter-group/view-filter-group.module.ts b/packages/twenty-server/src/engine/metadata-modules/view-filter-group/view-filter-group.module.ts index 561134830f..5a94797ff7 100644 --- a/packages/twenty-server/src/engine/metadata-modules/view-filter-group/view-filter-group.module.ts +++ b/packages/twenty-server/src/engine/metadata-modules/view-filter-group/view-filter-group.module.ts @@ -1,6 +1,8 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; +import { ApplicationModule } from 'src/engine/core-modules/application/application.module'; +import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.module'; import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module'; import { ViewFilterGroupController } from 'src/engine/metadata-modules/view-filter-group/controllers/view-filter-group.controller'; import { ViewFilterGroupEntity } from 'src/engine/metadata-modules/view-filter-group/entities/view-filter-group.entity'; @@ -9,12 +11,16 @@ import { ViewFilterGroupService } from 'src/engine/metadata-modules/view-filter- import { ViewPermissionsModule } from 'src/engine/metadata-modules/view-permissions/view-permissions.module'; import { ViewEntity } from 'src/engine/metadata-modules/view/entities/view.entity'; import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module'; +import { WorkspaceMigrationV2Module } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-v2.module'; @Module({ imports: [ TypeOrmModule.forFeature([ViewFilterGroupEntity, ViewEntity]), PermissionsModule, WorkspaceCacheStorageModule, + ApplicationModule, + WorkspaceMigrationV2Module, + WorkspaceManyOrAllFlatEntityMapsCacheModule, ViewPermissionsModule, ], controllers: [ViewFilterGroupController], diff --git a/packages/twenty-server/src/engine/metadata-modules/view-filter/services/view-filter.service.ts b/packages/twenty-server/src/engine/metadata-modules/view-filter/services/view-filter.service.ts index 69c00c7f96..063362bc19 100644 --- a/packages/twenty-server/src/engine/metadata-modules/view-filter/services/view-filter.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/view-filter/services/view-filter.service.ts @@ -74,11 +74,6 @@ export class ViewFilterService { ); } - this.flatEntityMapsCacheService.invalidateFlatEntityMaps({ - workspaceId, - flatMapsKeys: ['flatViewFilterMaps'], - }); - const { flatViewFilterMaps: recomputedExistingFlatViewFilterMaps } = await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps( { diff --git a/packages/twenty-server/src/engine/metadata-modules/view/utils/view-graphql-api-exception-handler.util.ts b/packages/twenty-server/src/engine/metadata-modules/view/utils/view-graphql-api-exception-handler.util.ts index 553d36f4bb..2643da33df 100644 --- a/packages/twenty-server/src/engine/metadata-modules/view/utils/view-graphql-api-exception-handler.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/view/utils/view-graphql-api-exception-handler.util.ts @@ -103,6 +103,14 @@ export const viewGraphqlApiExceptionHandler = (error: Error, i18n: I18n) => { throw new UserInputError(error.message, { userFriendlyMessage: error.userFriendlyMessage, }); + case ViewFilterGroupExceptionCode.CIRCULAR_DEPENDENCY: + throw new UserInputError(error.message, { + userFriendlyMessage: error.userFriendlyMessage, + }); + case ViewFilterGroupExceptionCode.MAX_DEPTH_EXCEEDED: + throw new UserInputError(error.message, { + userFriendlyMessage: error.userFriendlyMessage, + }); default: { return assertUnreachable(error.code); } diff --git a/packages/twenty-server/src/engine/workspace-cache/types/workspace-cache-key.type.ts b/packages/twenty-server/src/engine/workspace-cache/types/workspace-cache-key.type.ts index 13d684c682..f33731f5c7 100644 --- a/packages/twenty-server/src/engine/workspace-cache/types/workspace-cache-key.type.ts +++ b/packages/twenty-server/src/engine/workspace-cache/types/workspace-cache-key.type.ts @@ -17,6 +17,7 @@ export const WORKSPACE_CACHE_KEYS_V2 = { flatViewFieldMaps: 'flat-maps:view-field', flatViewGroupMaps: 'flat-maps:view-group', flatViewFilterMaps: 'flat-maps:view-filter', + flatViewFilterGroupMaps: 'flat-maps:view-filter-group', flatServerlessFunctionMaps: 'flat-maps:serverless-function', flatCronTriggerMaps: 'flat-maps:cron-trigger', flatDatabaseEventTriggerMaps: 'flat-maps:database-event-trigger', diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/twenty-standard-application-all-flat-entity-maps.constant.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/twenty-standard-application-all-flat-entity-maps.constant.ts index a369b6e148..0a9f3feb0d 100644 --- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/twenty-standard-application-all-flat-entity-maps.constant.ts +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/twenty-standard-application-all-flat-entity-maps.constant.ts @@ -78,10 +78,13 @@ export const computeTwentyStandardApplicationAllFlatEntityMaps = ({ workspaceId, }); + const flatViewFilterGroupMaps = createEmptyFlatEntityMaps(); + const flatViewFilterMaps = buildStandardFlatViewFilterMetadataMaps({ dependencyFlatEntityMaps: { flatFieldMetadataMaps, flatViewMaps, + flatViewFilterGroupMaps, }, now, standardObjectMetadataRelatedEntityIds, diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/view/create-standard-view-flat-metadata.util.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/view/create-standard-view-flat-metadata.util.ts index a05f7ce6a5..4358847802 100644 --- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/view/create-standard-view-flat-metadata.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/view/create-standard-view-flat-metadata.util.ts @@ -116,6 +116,7 @@ export const createStandardViewFlatMetadata = < viewFieldIds: [], viewFilterIds: [], viewGroupIds: [], + viewFilterGroupIds: [], createdAt: now, updatedAt: now, deletedAt: null, diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/interceptors/utils/from-workspace-migration-builder-exception-to-metadata-validation-response-error.util.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/interceptors/utils/from-workspace-migration-builder-exception-to-metadata-validation-response-error.util.ts index 2b23e57901..b2f887ee5a 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/interceptors/utils/from-workspace-migration-builder-exception-to-metadata-validation-response-error.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/interceptors/utils/from-workspace-migration-builder-exception-to-metadata-validation-response-error.util.ts @@ -19,6 +19,7 @@ export const fromWorkspaceMigrationBuilderExceptionToMetadataValidationResponseE summary: { invalidAgent: 0, invalidViewFilter: 0, + invalidViewFilterGroup: 0, invalidObjectMetadata: 0, invalidView: 0, invalidViewField: 0, diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/services/workspace-migration-build-orchestrator.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/services/workspace-migration-build-orchestrator.service.ts index 7a7b15332a..13f1d0d874 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/services/workspace-migration-build-orchestrator.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/services/workspace-migration-build-orchestrator.service.ts @@ -1,17 +1,17 @@ import { Injectable } from '@nestjs/common'; -import { AllMetadataName } from 'twenty-shared/metadata'; +import { type AllMetadataName } from 'twenty-shared/metadata'; import { isDefined } from 'twenty-shared/utils'; import { createEmptyAllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/constant/create-empty-all-flat-entity-maps.constant'; -import { AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type'; -import { MetadataFlatEntityAndRelatedFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/metadata-related-types.type'; +import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type'; +import { type MetadataFlatEntityAndRelatedFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/metadata-related-types.type'; import { createEmptyOrchestratorActionsReport } from 'src/engine/workspace-manager/workspace-migration-v2/constant/empty-orchestrator-actions-report.constant'; import { EMPTY_ORCHESTRATOR_FAILURE_REPORT } from 'src/engine/workspace-manager/workspace-migration-v2/constant/empty-orchestrator-failure-report.constant'; import { - WorkspaceMigrationOrchestratorBuildArgs, - WorkspaceMigrationOrchestratorFailedResult, - WorkspaceMigrationOrchestratorSuccessfulResult, + type WorkspaceMigrationOrchestratorBuildArgs, + type WorkspaceMigrationOrchestratorFailedResult, + type WorkspaceMigrationOrchestratorSuccessfulResult, } from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-orchestrator.type'; import { aggregateOrchestratorActionsReport } from 'src/engine/workspace-manager/workspace-migration-v2/utils/aggregate-orchestrator-actions-report.util'; import { WorkspaceMigrationV2AgentActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/agent/workspace-migration-v2-agent-actions-builder.service'; @@ -30,6 +30,7 @@ import { WorkspaceMigrationV2RowLevelPermissionPredicateGroupActionsBuilderServi import { WorkspaceMigrationV2RowLevelPermissionPredicateActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/row-level-permission-predicate/workspace-migration-v2-row-level-permission-predicate-actions-builder.service'; import { WorkspaceMigrationV2ServerlessFunctionActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/serverless-function/workspace-migration-v2-serverless-function-actions-builder.service'; import { WorkspaceMigrationV2ViewFieldActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view-field/workspace-migration-v2-view-field-actions-builder.service'; +import { WorkspaceMigrationV2ViewFilterGroupActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view-filter-group/workspace-migration-v2-view-filter-group-actions-builder.service'; import { WorkspaceMigrationV2ViewFilterActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view-filter/workspace-migration-v2-view-filter-actions-builder.service'; import { WorkspaceMigrationV2ViewGroupActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view-group/workspace-migration-v2-view-group-actions-builder.service'; import { WorkspaceMigrationV2ViewActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view/workspace-migration-v2-view-actions-builder.service'; @@ -42,6 +43,7 @@ export class WorkspaceMigrationBuildOrchestratorService { private readonly workspaceMigrationV2ViewActionsBuilderService: WorkspaceMigrationV2ViewActionsBuilderService, private readonly workspaceMigrationV2ViewFieldActionsBuilderService: WorkspaceMigrationV2ViewFieldActionsBuilderService, private readonly workspaceMigrationV2ViewFilterActionsBuilderService: WorkspaceMigrationV2ViewFilterActionsBuilderService, + private readonly workspaceMigrationV2ViewFilterGroupActionsBuilderService: WorkspaceMigrationV2ViewFilterGroupActionsBuilderService, private readonly workspaceMigrationV2ViewGroupActionsBuilderService: WorkspaceMigrationV2ViewGroupActionsBuilderService, private readonly workspaceMigrationV2ServerlessFunctionActionsBuilderService: WorkspaceMigrationV2ServerlessFunctionActionsBuilderService, private readonly workspaceMigrationV2DatabaseEventTriggerActionsBuilderService: WorkspaceMigrationV2DatabaseEventTriggerActionsBuilderService, @@ -144,6 +146,7 @@ export class WorkspaceMigrationBuildOrchestratorService { flatRouteTriggerMaps, flatFieldMetadataMaps, flatViewFilterMaps, + flatViewFilterGroupMaps, flatViewGroupMaps, flatRowLevelPermissionPredicateMaps, flatRowLevelPermissionPredicateGroupMaps, @@ -347,6 +350,46 @@ export class WorkspaceMigrationBuildOrchestratorService { } } + if (isDefined(flatViewFilterGroupMaps)) { + const { + from: fromFlatViewFilterGroupMaps, + to: toFlatViewFilterGroupMaps, + } = flatViewFilterGroupMaps; + + const viewFilterGroupResult = + await this.workspaceMigrationV2ViewFilterGroupActionsBuilderService.validateAndBuild( + { + additionalCacheDataMaps, + from: fromFlatViewFilterGroupMaps, + to: toFlatViewFilterGroupMaps, + buildOptions, + dependencyOptimisticFlatEntityMaps: { + flatViewMaps: optimisticAllFlatEntityMaps.flatViewMaps, + flatViewFilterGroupMaps: + optimisticAllFlatEntityMaps.flatViewFilterGroupMaps, + }, + workspaceId, + }, + ); + + this.mergeFlatEntityMapsAndRelatedFlatEntityMapsInAllFlatEntityMapsThroughMutation( + { + allFlatEntityMaps: optimisticAllFlatEntityMaps, + flatEntityMapsAndRelatedFlatEntityMaps: + viewFilterGroupResult.optimisticFlatEntityMapsAndRelatedFlatEntityMaps, + }, + ); + + if (viewFilterGroupResult.status === 'fail') { + orchestratorFailureReport.viewFilterGroup.push( + ...viewFilterGroupResult.errors, + ); + } else { + orchestratorActionsReport.viewFilterGroup = + viewFilterGroupResult.actions; + } + } + if (isDefined(flatViewFilterMaps)) { const { from: fromFlatViewFilterMaps, to: toFlatViewFilterMaps } = flatViewFilterMaps; @@ -361,6 +404,8 @@ export class WorkspaceMigrationBuildOrchestratorService { flatFieldMetadataMaps: optimisticAllFlatEntityMaps.flatFieldMetadataMaps, flatViewMaps: optimisticAllFlatEntityMaps.flatViewMaps, + flatViewFilterGroupMaps: + optimisticAllFlatEntityMaps.flatViewFilterGroupMaps, }, workspaceId, }, @@ -890,6 +935,9 @@ export class WorkspaceMigrationBuildOrchestratorService { ...aggregatedOrchestratorActionsReport.viewField.deleted, ...aggregatedOrchestratorActionsReport.viewField.created, ...aggregatedOrchestratorActionsReport.viewField.updated, + ...aggregatedOrchestratorActionsReport.viewFilterGroup.deleted, + ...aggregatedOrchestratorActionsReport.viewFilterGroup.created, + ...aggregatedOrchestratorActionsReport.viewFilterGroup.updated, ...aggregatedOrchestratorActionsReport.viewFilter.deleted, ...aggregatedOrchestratorActionsReport.viewFilter.created, ...aggregatedOrchestratorActionsReport.viewFilter.updated, diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/utils/__tests__/validate-flat-entity-circular-dependency.util.spec.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/utils/__tests__/validate-flat-entity-circular-dependency.util.spec.ts new file mode 100644 index 0000000000..4aeabe850f --- /dev/null +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/utils/__tests__/validate-flat-entity-circular-dependency.util.spec.ts @@ -0,0 +1,245 @@ +import { type EachTestingContext } from 'twenty-shared/testing'; + +import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type'; +import { type SyncableFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/flat-entity.type'; +import { + type CircularDependencyValidationResult, + validateFlatEntityCircularDependency, +} from 'src/engine/workspace-manager/workspace-migration-v2/utils/validate-flat-entity-circular-dependency.util'; + +type TestFlatEntity = SyncableFlatEntity & { + parentId: string | null; +}; + +const createFlatEntityMaps = ( + entities: TestFlatEntity[], +): FlatEntityMaps => ({ + byId: Object.fromEntries(entities.map((entity) => [entity.id, entity])), + idByUniversalIdentifier: Object.fromEntries( + entities.map((entity) => [entity.universalIdentifier, entity.id]), + ), + universalIdentifiersByApplicationId: {}, +}); + +const createTestEntity = ( + id: string, + parentId: string | null = null, +): TestFlatEntity => ({ + id, + parentId, + universalIdentifier: id, + applicationId: 'test-app', + workspaceId: 'test-workspace', +}); + +type TestContext = { + flatEntityId: string; + flatEntityParentId: string; + maxDepth?: number; + entities: TestFlatEntity[]; + expected: CircularDependencyValidationResult; +}; + +type ValidateCircularDependencyTestCase = EachTestingContext; + +const testCases: ValidateCircularDependencyTestCase[] = [ + { + title: 'should fail with self_reference when entity is its own parent', + context: { + flatEntityId: 'entity-1', + flatEntityParentId: 'entity-1', + entities: [], + expected: { + status: 'fail', + reason: 'self_reference', + }, + }, + }, + { + title: 'should succeed with depth 2 when parent has no parent', + context: { + flatEntityId: 'entity-child', + flatEntityParentId: 'entity-parent', + entities: [createTestEntity('entity-parent', null)], + expected: { + status: 'success', + depth: 2, + }, + }, + }, + { + title: 'should succeed with depth 3 when parent has a grandparent', + context: { + flatEntityId: 'entity-child', + flatEntityParentId: 'entity-parent', + entities: [ + createTestEntity('entity-grandparent', null), + createTestEntity('entity-parent', 'entity-grandparent'), + ], + expected: { + status: 'success', + depth: 3, + }, + }, + }, + { + title: + 'should fail with circular_dependency when parent points back to entity', + context: { + flatEntityId: 'entity-a', + flatEntityParentId: 'entity-b', + entities: [createTestEntity('entity-b', 'entity-a')], + expected: { + status: 'fail', + reason: 'circular_dependency', + depth: 2, + }, + }, + }, + { + title: + 'should fail with circular_dependency when chain creates a cycle (A -> B -> C -> A)', + context: { + flatEntityId: 'entity-a', + flatEntityParentId: 'entity-b', + entities: [ + createTestEntity('entity-b', 'entity-c'), + createTestEntity('entity-c', 'entity-a'), + ], + expected: { + status: 'fail', + reason: 'circular_dependency', + depth: 3, + }, + }, + }, + { + title: + 'should fail with circular_dependency when ancestors form a cycle among themselves', + context: { + flatEntityId: 'entity-new', + flatEntityParentId: 'entity-a', + entities: [ + createTestEntity('entity-a', 'entity-b'), + createTestEntity('entity-b', 'entity-a'), + ], + expected: { + status: 'fail', + reason: 'circular_dependency', + depth: 3, + }, + }, + }, + { + title: 'should succeed when parent does not exist in maps', + context: { + flatEntityId: 'entity-child', + flatEntityParentId: 'non-existent-parent', + entities: [], + expected: { + status: 'success', + depth: 1, + }, + }, + }, + { + title: 'should fail with max_depth_exceeded when depth exceeds maxDepth', + context: { + flatEntityId: 'entity-child', + flatEntityParentId: 'entity-parent', + maxDepth: 2, + entities: [ + createTestEntity('entity-grandparent', null), + createTestEntity('entity-parent', 'entity-grandparent'), + ], + expected: { + status: 'fail', + reason: 'max_depth_exceeded', + depth: 3, + }, + }, + }, + { + title: 'should succeed when depth equals maxDepth', + context: { + flatEntityId: 'entity-child', + flatEntityParentId: 'entity-parent', + maxDepth: 3, + entities: [ + createTestEntity('entity-grandparent', null), + createTestEntity('entity-parent', 'entity-grandparent'), + ], + expected: { + status: 'success', + depth: 3, + }, + }, + }, + { + title: 'should succeed with depth 2 when maxDepth is 2 and parent is root', + context: { + flatEntityId: 'entity-child', + flatEntityParentId: 'entity-parent', + maxDepth: 2, + entities: [createTestEntity('entity-parent', null)], + expected: { + status: 'success', + depth: 2, + }, + }, + }, + { + title: + 'should check max depth before circular dependency to prevent infinite loop', + context: { + flatEntityId: 'entity-new', + flatEntityParentId: 'entity-a', + maxDepth: 2, + entities: [ + createTestEntity('entity-a', 'entity-b'), + createTestEntity('entity-b', 'entity-c'), + createTestEntity('entity-c', 'entity-d'), + createTestEntity('entity-d', 'entity-a'), + ], + expected: { + status: 'fail', + reason: 'max_depth_exceeded', + depth: 3, + }, + }, + }, + { + title: 'should handle deep valid hierarchy without maxDepth', + context: { + flatEntityId: 'entity-new', + flatEntityParentId: 'entity-1', + entities: [ + createTestEntity('entity-5', null), + createTestEntity('entity-4', 'entity-5'), + createTestEntity('entity-3', 'entity-4'), + createTestEntity('entity-2', 'entity-3'), + createTestEntity('entity-1', 'entity-2'), + ], + expected: { + status: 'success', + depth: 6, + }, + }, + }, +]; + +describe('validateFlatEntityCircularDependency', () => { + test.each(testCases)('$title', ({ context }) => { + const flatEntityMaps = createFlatEntityMaps(context.entities); + + const result = validateFlatEntityCircularDependency({ + flatEntityId: context.flatEntityId, + flatEntityParentId: context.flatEntityParentId, + maxDepth: context.maxDepth, + parentIdKey: 'parentId', + flatEntityMaps, + }); + + expect(result).toEqual(context.expected); + }); +}); diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/utils/validate-flat-entity-circular-dependency.util.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/utils/validate-flat-entity-circular-dependency.util.ts new file mode 100644 index 0000000000..4aa82ce60b --- /dev/null +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/utils/validate-flat-entity-circular-dependency.util.ts @@ -0,0 +1,107 @@ +import { isDefined } from 'twenty-shared/utils'; + +import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type'; +import { type SyncableFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/flat-entity.type'; + +export type CircularDependencyValidationSuccess = { + status: 'success'; + depth: number; +}; + +export type CircularDependencyValidationFailure = { + status: 'fail'; + reason: 'self_reference' | 'circular_dependency' | 'max_depth_exceeded'; + depth?: number; +}; + +export type CircularDependencyValidationResult = + | CircularDependencyValidationSuccess + | CircularDependencyValidationFailure; + +export const validateFlatEntityCircularDependency = < + T extends SyncableFlatEntity, + K extends keyof T, +>({ + flatEntityId, + flatEntityParentId, + maxDepth, + parentIdKey, + flatEntityMaps, +}: { + flatEntityId: string; + flatEntityParentId: string; + maxDepth?: number; + parentIdKey: K; + flatEntityMaps: FlatEntityMaps; +}): CircularDependencyValidationResult => { + // Direct self-reference check + if (flatEntityId === flatEntityParentId) { + return { + status: 'fail', + reason: 'self_reference', + }; + } + + // Traverse ancestor chain to detect cycles and measure depth + const visited = new Set(); + let currentParentId: string | null | undefined = flatEntityParentId; + let depth = 1; + + while (isDefined(currentParentId)) { + if (isDefined(maxDepth) && depth > maxDepth) { + return { + status: 'fail', + reason: 'max_depth_exceeded', + depth, + }; + } + + // Check for circular dependency + if (currentParentId === flatEntityId) { + return { + status: 'fail', + reason: 'circular_dependency', + depth, + }; + } + + // Check for cycle in ancestors (already visited node) + if (visited.has(currentParentId)) { + return { + status: 'fail', + reason: 'circular_dependency', + depth, + }; + } + + visited.add(currentParentId); + + const parentEntity: T | undefined = flatEntityMaps.byId[currentParentId]; + + if (!isDefined(parentEntity)) { + break; + } + + const nextParentId: T[K] = parentEntity[parentIdKey]; + + currentParentId = + isDefined(nextParentId) && typeof nextParentId === 'string' + ? nextParentId + : undefined; + depth++; + } + + // Check max depth for the final depth value + if (isDefined(maxDepth) && depth > maxDepth) { + return { + status: 'fail', + reason: 'max_depth_exceeded', + depth, + }; + } + + return { + status: 'success', + depth, + }; +}; diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view-filter-group/types/workspace-migration-view-filter-group-action-v2.type.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view-filter-group/types/workspace-migration-view-filter-group-action-v2.type.ts new file mode 100644 index 0000000000..8a625f7522 --- /dev/null +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view-filter-group/types/workspace-migration-view-filter-group-action-v2.type.ts @@ -0,0 +1,26 @@ +import { type FlatEntityPropertiesUpdates } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-properties-updates.type'; +import { type FlatViewFilterGroup } from 'src/engine/metadata-modules/flat-view-filter-group/types/flat-view-filter-group.type'; + +export type CreateViewFilterGroupAction = { + type: 'create_view_filter_group'; + viewFilterGroup: FlatViewFilterGroup; +}; + +export type UpdateViewFilterGroupAction = { + type: 'update_view_filter_group'; + viewFilterGroupId: string; + updates: FlatEntityPropertiesUpdates<'viewFilterGroup'>; +}; + +export type DeleteViewFilterGroupAction = { + type: 'delete_view_filter_group'; + viewFilterGroupId: string; +}; + +export type WorkspaceMigrationViewFilterGroupActionV2 = + | CreateViewFilterGroupAction + | UpdateViewFilterGroupAction + | DeleteViewFilterGroupAction; + +export type WorkspaceMigrationViewFilterGroupActionTypeV2 = + WorkspaceMigrationViewFilterGroupActionV2['type']; diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view-filter-group/workspace-migration-v2-view-filter-group-actions-builder.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view-filter-group/workspace-migration-v2-view-filter-group-actions-builder.service.ts new file mode 100644 index 0000000000..0fa3c0353e --- /dev/null +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view-filter-group/workspace-migration-v2-view-filter-group-actions-builder.service.ts @@ -0,0 +1,113 @@ +import { Injectable } from '@nestjs/common'; + +import { ALL_METADATA_NAME } from 'twenty-shared/metadata'; + +import { type UpdateViewFilterGroupAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view-filter-group/types/workspace-migration-view-filter-group-action-v2.type'; +import { WorkspaceEntityMigrationBuilderV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/services/workspace-entity-migration-builder-v2.service'; +import { type FlatEntityUpdateValidationArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/flat-entity-update-validation-args.type'; +import { type FlatEntityValidationArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/flat-entity-validation-args.type'; +import { type FlatEntityValidationReturnType } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/flat-entity-validation-result.type'; +import { FlatViewFilterGroupValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-view-filter-group-validator.service'; + +@Injectable() +export class WorkspaceMigrationV2ViewFilterGroupActionsBuilderService extends WorkspaceEntityMigrationBuilderV2Service< + typeof ALL_METADATA_NAME.viewFilterGroup +> { + constructor( + private readonly flatViewFilterGroupValidatorService: FlatViewFilterGroupValidatorService, + ) { + super(ALL_METADATA_NAME.viewFilterGroup); + } + + protected validateFlatEntityCreation( + args: FlatEntityValidationArgs, + ): FlatEntityValidationReturnType< + typeof ALL_METADATA_NAME.viewFilterGroup, + 'created' + > { + const validationResult = + this.flatViewFilterGroupValidatorService.validateFlatViewFilterGroupCreation( + args, + ); + + if (validationResult.errors.length > 0) { + return { + status: 'fail', + ...validationResult, + }; + } + + const { flatEntityToValidate: flatViewFilterGroupToValidate } = args; + + return { + status: 'success', + action: { + type: 'create_view_filter_group', + viewFilterGroup: flatViewFilterGroupToValidate, + }, + }; + } + + protected validateFlatEntityDeletion( + args: FlatEntityValidationArgs, + ): FlatEntityValidationReturnType< + typeof ALL_METADATA_NAME.viewFilterGroup, + 'deleted' + > { + const validationResult = + this.flatViewFilterGroupValidatorService.validateFlatViewFilterGroupDeletion( + args, + ); + + if (validationResult.errors.length > 0) { + return { + status: 'fail', + ...validationResult, + }; + } + + const { flatEntityToValidate: flatViewFilterGroupToValidate } = args; + + return { + status: 'success', + action: { + type: 'delete_view_filter_group', + viewFilterGroupId: flatViewFilterGroupToValidate.id, + }, + }; + } + + protected validateFlatEntityUpdate( + args: FlatEntityUpdateValidationArgs< + typeof ALL_METADATA_NAME.viewFilterGroup + >, + ): FlatEntityValidationReturnType< + typeof ALL_METADATA_NAME.viewFilterGroup, + 'updated' + > { + const validationResult = + this.flatViewFilterGroupValidatorService.validateFlatViewFilterGroupUpdate( + args, + ); + + if (validationResult.errors.length > 0) { + return { + status: 'fail', + ...validationResult, + }; + } + + const { flatEntityId, flatEntityUpdates } = args; + + const updateViewFilterGroupAction: UpdateViewFilterGroupAction = { + type: 'update_view_filter_group', + viewFilterGroupId: flatEntityId, + updates: flatEntityUpdates, + }; + + return { + status: 'success', + action: updateViewFilterGroupAction, + }; + } +} diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-view-filter-group-validator.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-view-filter-group-validator.service.ts new file mode 100644 index 0000000000..0d9cae1d96 --- /dev/null +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-view-filter-group-validator.service.ts @@ -0,0 +1,260 @@ +import { Injectable } from '@nestjs/common'; + +import { msg, t } from '@lingui/core/macro'; +import { type ALL_METADATA_NAME } from 'twenty-shared/metadata'; +import { isDefined } from 'twenty-shared/utils'; + +import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util'; +import { type FlatViewFilterGroupMaps } from 'src/engine/metadata-modules/flat-view-filter-group/types/flat-view-filter-group-maps.type'; +import { type FlatViewFilterGroup } from 'src/engine/metadata-modules/flat-view-filter-group/types/flat-view-filter-group.type'; +import { ViewFilterGroupExceptionCode } from 'src/engine/metadata-modules/view-filter-group/exceptions/view-filter-group.exception'; +import { findFlatEntityPropertyUpdate } from 'src/engine/workspace-manager/workspace-migration-v2/utils/find-flat-entity-property-update.util'; +import { validateFlatEntityCircularDependency } from 'src/engine/workspace-manager/workspace-migration-v2/utils/validate-flat-entity-circular-dependency.util'; +import { + type FailedFlatEntityValidation, + type FlatEntityValidationError, +} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/types/failed-flat-entity-validation.type'; +import { type FlatEntityUpdateValidationArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/flat-entity-update-validation-args.type'; +import { type FlatEntityValidationArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/flat-entity-validation-args.type'; + +// View filter groups can have at most 2 levels of nesting (root and one child level) +const VIEW_FILTER_GROUP_MAX_DEPTH = 2; + +@Injectable() +export class FlatViewFilterGroupValidatorService { + constructor() {} + + private getCircularDependencyValidationErrors({ + viewFilterGroupId, + parentViewFilterGroupId, + flatViewFilterGroupMaps, + }: { + viewFilterGroupId: string; + parentViewFilterGroupId: string; + flatViewFilterGroupMaps: FlatViewFilterGroupMaps; + }): FlatEntityValidationError[] { + const circularDependencyResult = validateFlatEntityCircularDependency({ + flatEntityId: viewFilterGroupId, + flatEntityParentId: parentViewFilterGroupId, + maxDepth: VIEW_FILTER_GROUP_MAX_DEPTH, + parentIdKey: 'parentViewFilterGroupId', + flatEntityMaps: flatViewFilterGroupMaps, + }); + + if (circularDependencyResult.status === 'success') { + return []; + } + + switch (circularDependencyResult.reason) { + case 'self_reference': + return [ + { + code: ViewFilterGroupExceptionCode.CIRCULAR_DEPENDENCY, + message: t`View filter group cannot be its own parent`, + userFriendlyMessage: msg`View filter group cannot be its own parent`, + }, + ]; + case 'circular_dependency': + return [ + { + code: ViewFilterGroupExceptionCode.CIRCULAR_DEPENDENCY, + message: t`Circular dependency detected in view filter group hierarchy`, + userFriendlyMessage: msg`Circular dependency detected in view filter group hierarchy`, + }, + ]; + case 'max_depth_exceeded': + return [ + { + code: ViewFilterGroupExceptionCode.MAX_DEPTH_EXCEEDED, + message: t`View filter group hierarchy exceeds maximum depth of ${VIEW_FILTER_GROUP_MAX_DEPTH}`, + userFriendlyMessage: msg`View filter group hierarchy exceeds maximum depth of ${VIEW_FILTER_GROUP_MAX_DEPTH}`, + }, + ]; + } + } + + validateFlatViewFilterGroupCreation({ + flatEntityToValidate: flatViewFilterGroupToValidate, + optimisticFlatEntityMapsAndRelatedFlatEntityMaps: { + flatViewFilterGroupMaps: optimisticFlatViewFilterGroupMaps, + flatViewMaps, + }, + remainingFlatEntityMapsToValidate, + }: FlatEntityValidationArgs< + typeof ALL_METADATA_NAME.viewFilterGroup + >): FailedFlatEntityValidation { + const validationResult: FailedFlatEntityValidation = { + type: 'create_view_filter_group', + errors: [], + flatEntityMinimalInformation: { + id: flatViewFilterGroupToValidate.id, + }, + }; + + const existingViewFilterGroup = findFlatEntityByIdInFlatEntityMaps({ + flatEntityId: flatViewFilterGroupToValidate.id, + flatEntityMaps: optimisticFlatViewFilterGroupMaps, + }); + + if (isDefined(existingViewFilterGroup)) { + validationResult.errors.push({ + code: ViewFilterGroupExceptionCode.INVALID_VIEW_FILTER_GROUP_DATA, + message: t`View filter group with this id already exists`, + userFriendlyMessage: msg`View filter group with this id already exists`, + }); + } + + const referencedView = findFlatEntityByIdInFlatEntityMaps({ + flatEntityId: flatViewFilterGroupToValidate.viewId, + flatEntityMaps: flatViewMaps, + }); + + if (!isDefined(referencedView)) { + validationResult.errors.push({ + code: ViewFilterGroupExceptionCode.VIEW_NOT_FOUND, + message: t`View not found`, + userFriendlyMessage: msg`View not found`, + }); + } + + if (isDefined(flatViewFilterGroupToValidate.parentViewFilterGroupId)) { + const circularDependencyErrors = + this.getCircularDependencyValidationErrors({ + viewFilterGroupId: flatViewFilterGroupToValidate.id, + parentViewFilterGroupId: + flatViewFilterGroupToValidate.parentViewFilterGroupId, + flatViewFilterGroupMaps: optimisticFlatViewFilterGroupMaps, + }); + + if (circularDependencyErrors.length > 0) { + validationResult.errors.push(...circularDependencyErrors); + } + + const referencedParentInOptimistic = findFlatEntityByIdInFlatEntityMaps({ + flatEntityId: flatViewFilterGroupToValidate.parentViewFilterGroupId, + flatEntityMaps: optimisticFlatViewFilterGroupMaps, + }); + + const referencedParentInRemaining = findFlatEntityByIdInFlatEntityMaps({ + flatEntityId: flatViewFilterGroupToValidate.parentViewFilterGroupId, + flatEntityMaps: remainingFlatEntityMapsToValidate, + }); + + if ( + !isDefined(referencedParentInOptimistic) && + !isDefined(referencedParentInRemaining) + ) { + validationResult.errors.push({ + code: ViewFilterGroupExceptionCode.VIEW_FILTER_GROUP_NOT_FOUND, + message: t`Parent view filter group not found`, + userFriendlyMessage: msg`Parent view filter group not found`, + }); + } + } + + return validationResult; + } + + validateFlatViewFilterGroupDeletion({ + flatEntityToValidate: flatViewFilterGroupToValidate, + optimisticFlatEntityMapsAndRelatedFlatEntityMaps: { + flatViewFilterGroupMaps: optimisticFlatViewFilterGroupMaps, + }, + }: FlatEntityValidationArgs< + typeof ALL_METADATA_NAME.viewFilterGroup + >): FailedFlatEntityValidation { + const validationResult: FailedFlatEntityValidation = { + type: 'delete_view_filter_group', + errors: [], + flatEntityMinimalInformation: { + id: flatViewFilterGroupToValidate.id, + }, + }; + + const existingViewFilterGroup = findFlatEntityByIdInFlatEntityMaps({ + flatEntityId: flatViewFilterGroupToValidate.id, + flatEntityMaps: optimisticFlatViewFilterGroupMaps, + }); + + if (!isDefined(existingViewFilterGroup)) { + validationResult.errors.push({ + code: ViewFilterGroupExceptionCode.VIEW_FILTER_GROUP_NOT_FOUND, + message: t`View filter group not found`, + userFriendlyMessage: msg`View filter group not found`, + }); + } + + return validationResult; + } + + validateFlatViewFilterGroupUpdate({ + flatEntityId, + flatEntityUpdates, + optimisticFlatEntityMapsAndRelatedFlatEntityMaps: { + flatViewFilterGroupMaps: optimisticFlatViewFilterGroupMaps, + }, + }: FlatEntityUpdateValidationArgs< + typeof ALL_METADATA_NAME.viewFilterGroup + >): FailedFlatEntityValidation { + const validationResult: FailedFlatEntityValidation = { + type: 'update_view_filter_group', + errors: [], + flatEntityMinimalInformation: { + id: flatEntityId, + }, + }; + + const existingViewFilterGroup = + optimisticFlatViewFilterGroupMaps.byId[flatEntityId]; + + if (!isDefined(existingViewFilterGroup)) { + validationResult.errors.push({ + code: ViewFilterGroupExceptionCode.VIEW_FILTER_GROUP_NOT_FOUND, + message: t`View filter group not found`, + userFriendlyMessage: msg`View filter group not found`, + }); + } + + const parentViewFilterGroupIdUpdate = findFlatEntityPropertyUpdate({ + property: 'parentViewFilterGroupId', + flatEntityUpdates, + }); + + if (!isDefined(parentViewFilterGroupIdUpdate)) { + return validationResult; + } + + const newParentViewFilterGroupId = parentViewFilterGroupIdUpdate.to; + + if (!isDefined(newParentViewFilterGroupId)) { + return validationResult; + } + + const circularDependencyErrors = this.getCircularDependencyValidationErrors( + { + viewFilterGroupId: flatEntityId, + parentViewFilterGroupId: newParentViewFilterGroupId, + flatViewFilterGroupMaps: optimisticFlatViewFilterGroupMaps, + }, + ); + + if (circularDependencyErrors.length > 0) { + validationResult.errors.push(...circularDependencyErrors); + } + + const referencedParentViewFilterGroup = findFlatEntityByIdInFlatEntityMaps({ + flatEntityId: newParentViewFilterGroupId, + flatEntityMaps: optimisticFlatViewFilterGroupMaps, + }); + + if (!isDefined(referencedParentViewFilterGroup)) { + validationResult.errors.push({ + code: ViewFilterGroupExceptionCode.VIEW_FILTER_GROUP_NOT_FOUND, + message: t`Parent view filter group not found`, + userFriendlyMessage: msg`Parent view filter group not found`, + }); + } + + return validationResult; + } +} diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-view-filter-validator.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-view-filter-validator.service.ts index 7cf700ce7d..df53cf8d2e 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-view-filter-validator.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-view-filter-validator.service.ts @@ -1,15 +1,15 @@ import { Injectable } from '@nestjs/common'; import { msg, t } from '@lingui/core/macro'; -import { ALL_METADATA_NAME } from 'twenty-shared/metadata'; +import { type ALL_METADATA_NAME } from 'twenty-shared/metadata'; import { isDefined } from 'twenty-shared/utils'; import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util'; import { type FlatViewFilter } from 'src/engine/metadata-modules/flat-view-filter/types/flat-view-filter.type'; import { ViewFilterExceptionCode } from 'src/engine/metadata-modules/view-filter/exceptions/view-filter.exception'; -import { FailedFlatEntityValidation } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/types/failed-flat-entity-validation.type'; -import { FlatEntityUpdateValidationArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/flat-entity-update-validation-args.type'; -import { FlatEntityValidationArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/flat-entity-validation-args.type'; +import { type FailedFlatEntityValidation } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/types/failed-flat-entity-validation.type'; +import { type FlatEntityUpdateValidationArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/flat-entity-update-validation-args.type'; +import { type FlatEntityValidationArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/flat-entity-validation-args.type'; import { fromFlatEntityPropertiesUpdatesToPartialFlatEntity } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/utils/from-flat-entity-properties-updates-to-partial-flat-entity'; @Injectable() @@ -22,6 +22,7 @@ export class FlatViewFilterValidatorService { flatViewFilterMaps: optimisticFlatViewFilterMaps, flatViewMaps, flatFieldMetadataMaps, + flatViewFilterGroupMaps, }, }: FlatEntityValidationArgs< typeof ALL_METADATA_NAME.viewFilter @@ -73,6 +74,21 @@ export class FlatViewFilterValidatorService { }); } + if (isDefined(flatViewFilterToValidate.viewFilterGroupId)) { + const referencedViewFilterGroup = findFlatEntityByIdInFlatEntityMaps({ + flatEntityId: flatViewFilterToValidate.viewFilterGroupId, + flatEntityMaps: flatViewFilterGroupMaps, + }); + + if (!isDefined(referencedViewFilterGroup)) { + validationResult.errors.push({ + code: ViewFilterExceptionCode.INVALID_VIEW_FILTER_DATA, + message: t`View filter group not found`, + userFriendlyMessage: msg`View filter group not found`, + }); + } + } + return validationResult; } @@ -116,6 +132,7 @@ export class FlatViewFilterValidatorService { optimisticFlatEntityMapsAndRelatedFlatEntityMaps: { flatViewFilterMaps: optimisticFlatViewFilterMaps, flatFieldMetadataMaps, + flatViewFilterGroupMaps, }, }: FlatEntityUpdateValidationArgs< typeof ALL_METADATA_NAME.viewFilter @@ -160,6 +177,21 @@ export class FlatViewFilterValidatorService { }); } + if (isDefined(updatedFlatViewFilter.viewFilterGroupId)) { + const referencedViewFilterGroup = findFlatEntityByIdInFlatEntityMaps({ + flatEntityId: updatedFlatViewFilter.viewFilterGroupId, + flatEntityMaps: flatViewFilterGroupMaps, + }); + + if (!isDefined(referencedViewFilterGroup)) { + validationResult.errors.push({ + code: ViewFilterExceptionCode.INVALID_VIEW_FILTER_DATA, + message: t`View filter group not found`, + userFriendlyMessage: msg`View filter group not found`, + }); + } + } + return validationResult; } } diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/workspace-migration-builder-validators.module.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/workspace-migration-builder-validators.module.ts index cb17a8ad21..8bbb7bfd8c 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/workspace-migration-builder-validators.module.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/workspace-migration-builder-validators.module.ts @@ -18,6 +18,7 @@ import { FlatRowLevelPermissionPredicateGroupValidatorService } from 'src/engine import { FlatRowLevelPermissionPredicateValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-row-level-permission-predicate-validator.service'; import { FlatServerlessFunctionValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-serverless-function-validator.service'; import { FlatViewFieldValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-view-field-validator.service'; +import { FlatViewFilterGroupValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-view-filter-group-validator.service'; import { FlatViewFilterValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-view-filter-validator.service'; import { FlatViewGroupValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-view-group-validator.service'; import { FlatViewValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-view-validator.service'; @@ -28,6 +29,7 @@ import { FlatViewValidatorService } from 'src/engine/workspace-manager/workspace FlatViewValidatorService, FlatViewFieldValidatorService, FlatViewFilterValidatorService, + FlatViewFilterGroupValidatorService, FlatViewGroupValidatorService, FlatIndexValidatorService, FlatFieldMetadataValidatorService, @@ -50,6 +52,7 @@ import { FlatViewValidatorService } from 'src/engine/workspace-manager/workspace FlatViewValidatorService, FlatViewFieldValidatorService, FlatViewFilterValidatorService, + FlatViewFilterGroupValidatorService, FlatViewGroupValidatorService, FlatIndexValidatorService, FlatFieldMetadataValidatorService, diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-builder-v2.module.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-builder-v2.module.ts index e9d775b5d2..ca65224935 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-builder-v2.module.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-builder-v2.module.ts @@ -19,6 +19,7 @@ import { WorkspaceMigrationV2RowLevelPermissionPredicateActionsBuilderService } import { WorkspaceMigrationV2ServerlessFunctionActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/serverless-function/workspace-migration-v2-serverless-function-actions-builder.service'; import { WorkspaceMigrationV2ViewFieldActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view-field/workspace-migration-v2-view-field-actions-builder.service'; import { WorkspaceMigrationV2ViewFilterActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view-filter/workspace-migration-v2-view-filter-actions-builder.service'; +import { WorkspaceMigrationV2ViewFilterGroupActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view-filter-group/workspace-migration-v2-view-filter-group-actions-builder.service'; import { WorkspaceMigrationV2ViewGroupActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view-group/workspace-migration-v2-view-group-actions-builder.service'; import { WorkspaceMigrationV2ViewActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view/workspace-migration-v2-view-actions-builder.service'; import { WorkspaceMigrationBuilderValidatorsModule } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/workspace-migration-builder-validators.module'; @@ -31,6 +32,7 @@ import { WorkspaceMigrationBuilderValidatorsModule } from 'src/engine/workspace- WorkspaceMigrationV2ViewActionsBuilderService, WorkspaceMigrationV2ViewFieldActionsBuilderService, WorkspaceMigrationV2ViewFilterActionsBuilderService, + WorkspaceMigrationV2ViewFilterGroupActionsBuilderService, WorkspaceMigrationV2ViewGroupActionsBuilderService, WorkspaceMigrationV2IndexActionsBuilderService, WorkspaceMigrationV2ServerlessFunctionActionsBuilderService, @@ -53,6 +55,7 @@ import { WorkspaceMigrationBuilderValidatorsModule } from 'src/engine/workspace- WorkspaceMigrationV2ObjectActionsBuilderService, WorkspaceMigrationV2ViewFieldActionsBuilderService, WorkspaceMigrationV2ViewFilterActionsBuilderService, + WorkspaceMigrationV2ViewFilterGroupActionsBuilderService, WorkspaceMigrationV2ViewGroupActionsBuilderService, WorkspaceMigrationV2FieldActionsBuilderService, WorkspaceMigrationV2ServerlessFunctionActionsBuilderService, diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/view-filter-group/services/create-view-filter-group-action-handler.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/view-filter-group/services/create-view-filter-group-action-handler.service.ts new file mode 100644 index 0000000000..09a5f1365c --- /dev/null +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/view-filter-group/services/create-view-filter-group-action-handler.service.ts @@ -0,0 +1,59 @@ +import { Injectable } from '@nestjs/common'; + +import { + type OptimisticallyApplyActionOnAllFlatEntityMapsArgs, + WorkspaceMigrationRunnerActionHandler, +} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface'; + +import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type'; +import { addFlatEntityToFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/add-flat-entity-to-flat-entity-maps-or-throw.util'; +import { ViewFilterGroupEntity } from 'src/engine/metadata-modules/view-filter-group/entities/view-filter-group.entity'; +import { type CreateViewFilterGroupAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view-filter-group/types/workspace-migration-view-filter-group-action-v2.type'; +import { type WorkspaceMigrationActionRunnerArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/types/workspace-migration-action-runner-args.type'; + +@Injectable() +export class CreateViewFilterGroupActionHandlerService extends WorkspaceMigrationRunnerActionHandler( + 'create_view_filter_group', +) { + optimisticallyApplyActionOnAllFlatEntityMaps({ + action, + allFlatEntityMaps, + }: OptimisticallyApplyActionOnAllFlatEntityMapsArgs): Partial { + const { flatViewFilterGroupMaps } = allFlatEntityMaps; + const { viewFilterGroup } = action; + + const updatedFlatViewFilterGroupMaps = addFlatEntityToFlatEntityMapsOrThrow( + { + flatEntity: viewFilterGroup, + flatEntityMaps: flatViewFilterGroupMaps, + }, + ); + + return { + flatViewFilterGroupMaps: updatedFlatViewFilterGroupMaps, + }; + } + + async executeForMetadata( + context: WorkspaceMigrationActionRunnerArgs, + ): Promise { + const { action, queryRunner, workspaceId } = context; + const { viewFilterGroup } = action; + + const viewFilterGroupRepository = + queryRunner.manager.getRepository( + ViewFilterGroupEntity, + ); + + await viewFilterGroupRepository.insert({ + ...viewFilterGroup, + workspaceId, + }); + } + + async executeForWorkspaceSchema( + _context: WorkspaceMigrationActionRunnerArgs, + ): Promise { + return; + } +} diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/view-filter-group/services/delete-view-filter-group-action-handler.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/view-filter-group/services/delete-view-filter-group-action-handler.service.ts new file mode 100644 index 0000000000..83edd69f74 --- /dev/null +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/view-filter-group/services/delete-view-filter-group-action-handler.service.ts @@ -0,0 +1,62 @@ +import { Injectable } from '@nestjs/common'; + +import { + type OptimisticallyApplyActionOnAllFlatEntityMapsArgs, + WorkspaceMigrationRunnerActionHandler, +} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface'; + +import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type'; +import { deleteFlatEntityFromFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/delete-flat-entity-from-flat-entity-maps-or-throw.util'; +import { ViewFilterGroupEntity } from 'src/engine/metadata-modules/view-filter-group/entities/view-filter-group.entity'; +import { type DeleteViewFilterGroupAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view-filter-group/types/workspace-migration-view-filter-group-action-v2.type'; +import { type WorkspaceMigrationActionRunnerArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/types/workspace-migration-action-runner-args.type'; + +@Injectable() +export class DeleteViewFilterGroupActionHandlerService extends WorkspaceMigrationRunnerActionHandler( + 'delete_view_filter_group', +) { + constructor() { + super(); + } + + optimisticallyApplyActionOnAllFlatEntityMaps({ + action, + allFlatEntityMaps, + }: OptimisticallyApplyActionOnAllFlatEntityMapsArgs): Partial { + const { flatViewFilterGroupMaps } = allFlatEntityMaps; + const { viewFilterGroupId } = action; + + const updatedFlatViewFilterGroupMaps = + deleteFlatEntityFromFlatEntityMapsOrThrow({ + entityToDeleteId: viewFilterGroupId, + flatEntityMaps: flatViewFilterGroupMaps, + }); + + return { + flatViewFilterGroupMaps: updatedFlatViewFilterGroupMaps, + }; + } + + async executeForMetadata( + context: WorkspaceMigrationActionRunnerArgs, + ): Promise { + const { action, queryRunner, workspaceId } = context; + const { viewFilterGroupId } = action; + + const viewFilterGroupRepository = + queryRunner.manager.getRepository( + ViewFilterGroupEntity, + ); + + await viewFilterGroupRepository.delete({ + id: viewFilterGroupId, + workspaceId, + }); + } + + async executeForWorkspaceSchema( + _context: WorkspaceMigrationActionRunnerArgs, + ): Promise { + return; + } +} diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/view-filter-group/services/update-view-filter-group-action-handler.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/view-filter-group/services/update-view-filter-group-action-handler.service.ts new file mode 100644 index 0000000000..e20222b787 --- /dev/null +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/view-filter-group/services/update-view-filter-group-action-handler.service.ts @@ -0,0 +1,73 @@ +import { Injectable } from '@nestjs/common'; + +import { + type OptimisticallyApplyActionOnAllFlatEntityMapsArgs, + WorkspaceMigrationRunnerActionHandler, +} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface'; + +import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type'; +import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util'; +import { replaceFlatEntityInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/replace-flat-entity-in-flat-entity-maps-or-throw.util'; +import { ViewFilterGroupEntity } from 'src/engine/metadata-modules/view-filter-group/entities/view-filter-group.entity'; +import { type UpdateViewFilterGroupAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view-filter-group/types/workspace-migration-view-filter-group-action-v2.type'; +import { type WorkspaceMigrationActionRunnerArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/types/workspace-migration-action-runner-args.type'; +import { fromFlatEntityPropertiesUpdatesToPartialFlatEntity } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/utils/from-flat-entity-properties-updates-to-partial-flat-entity'; + +@Injectable() +export class UpdateViewFilterGroupActionHandlerService extends WorkspaceMigrationRunnerActionHandler( + 'update_view_filter_group', +) { + constructor() { + super(); + } + + optimisticallyApplyActionOnAllFlatEntityMaps({ + action, + allFlatEntityMaps, + }: OptimisticallyApplyActionOnAllFlatEntityMapsArgs): Partial { + const { flatViewFilterGroupMaps } = allFlatEntityMaps; + const { viewFilterGroupId } = action; + + const existingViewFilterGroup = findFlatEntityByIdInFlatEntityMapsOrThrow({ + flatEntityId: viewFilterGroupId, + flatEntityMaps: flatViewFilterGroupMaps, + }); + + const updatedViewFilterGroup = { + ...existingViewFilterGroup, + ...fromFlatEntityPropertiesUpdatesToPartialFlatEntity(action), + }; + + const updatedFlatViewFilterGroupMaps = + replaceFlatEntityInFlatEntityMapsOrThrow({ + flatEntity: updatedViewFilterGroup, + flatEntityMaps: flatViewFilterGroupMaps, + }); + + return { + flatViewFilterGroupMaps: updatedFlatViewFilterGroupMaps, + }; + } + + async executeForMetadata( + context: WorkspaceMigrationActionRunnerArgs, + ): Promise { + const { action, queryRunner } = context; + const { viewFilterGroupId } = action; + + const viewFilterGroupRepository = + queryRunner.manager.getRepository( + ViewFilterGroupEntity, + ); + + const update = fromFlatEntityPropertiesUpdatesToPartialFlatEntity(action); + + await viewFilterGroupRepository.update(viewFilterGroupId, update); + } + + async executeForWorkspaceSchema( + _context: WorkspaceMigrationActionRunnerArgs, + ): Promise { + return; + } +} diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/workspace-schema-migration-runner-action-handlers.module.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/workspace-schema-migration-runner-action-handlers.module.ts index 7f023515e8..fff9d9b1e6 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/workspace-schema-migration-runner-action-handlers.module.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/workspace-schema-migration-runner-action-handlers.module.ts @@ -51,6 +51,9 @@ import { UpdateViewFieldActionHandlerService } from 'src/engine/workspace-manage import { CreateViewFilterActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/view-filter/services/create-view-filter-action-handler.service'; import { DeleteViewFilterActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/view-filter/services/delete-view-filter-action-handler.service'; import { UpdateViewFilterActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/view-filter/services/update-view-filter-action-handler.service'; +import { CreateViewFilterGroupActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/view-filter-group/services/create-view-filter-group-action-handler.service'; +import { DeleteViewFilterGroupActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/view-filter-group/services/delete-view-filter-group-action-handler.service'; +import { UpdateViewFilterGroupActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/view-filter-group/services/update-view-filter-group-action-handler.service'; import { CreateViewGroupActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/view-group/services/create-view-group-action-handler.service'; import { DeleteViewGroupActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/view-group/services/delete-view-group-action-handler.service'; import { UpdateViewGroupActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/view-group/services/update-view-group-action-handler.service'; @@ -84,6 +87,10 @@ import { UpdateViewActionHandlerService } from 'src/engine/workspace-manager/wor UpdateViewFilterActionHandlerService, DeleteViewFilterActionHandlerService, + CreateViewFilterGroupActionHandlerService, + UpdateViewFilterGroupActionHandlerService, + DeleteViewFilterGroupActionHandlerService, + CreateViewGroupActionHandlerService, UpdateViewGroupActionHandlerService, DeleteViewGroupActionHandlerService, diff --git a/packages/twenty-server/test/integration/constants/view-gql-fields.constants.ts b/packages/twenty-server/test/integration/constants/view-gql-fields.constants.ts index fd96c59106..d2021a785a 100644 --- a/packages/twenty-server/test/integration/constants/view-gql-fields.constants.ts +++ b/packages/twenty-server/test/integration/constants/view-gql-fields.constants.ts @@ -64,6 +64,7 @@ export const VIEW_FILTER_GROUP_GQL_FIELDS = ` id logicalOperator parentViewFilterGroupId + positionInViewFilterGroup viewId createdAt updatedAt diff --git a/packages/twenty-server/test/integration/graphql/suites/view/view-field/__snapshots__/failing-create-many-view-fields-v2.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/view/view-field/__snapshots__/failing-create-many-view-fields-v2.integration-spec.ts.snap index 1e42b92a88..0772853afc 100644 --- a/packages/twenty-server/test/integration/graphql/suites/view/view-field/__snapshots__/failing-create-many-view-fields-v2.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/view/view-field/__snapshots__/failing-create-many-view-fields-v2.integration-spec.ts.snap @@ -77,6 +77,7 @@ exports[`View Field Resolver - Failing Create Many Operations - v2 should accumu }, ], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -99,6 +100,7 @@ exports[`View Field Resolver - Failing Create Many Operations - v2 should accumu "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, diff --git a/packages/twenty-server/test/integration/graphql/suites/view/view-field/__snapshots__/failing-create-view-field-v2.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/view/view-field/__snapshots__/failing-create-view-field-v2.integration-spec.ts.snap index 4fe3a46f92..fc38c8bfe1 100644 --- a/packages/twenty-server/test/integration/graphql/suites/view/view-field/__snapshots__/failing-create-view-field-v2.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/view/view-field/__snapshots__/failing-create-view-field-v2.integration-spec.ts.snap @@ -40,6 +40,7 @@ exports[`View Field Resolver - Failing Create Operation - v2 Should fail to crea }, ], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -62,6 +63,7 @@ exports[`View Field Resolver - Failing Create Operation - v2 Should fail to crea "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -112,6 +114,7 @@ exports[`View Field Resolver - Failing Create Operation - v2 should fail to crea }, ], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -134,6 +137,7 @@ exports[`View Field Resolver - Failing Create Operation - v2 should fail to crea "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -184,6 +188,7 @@ exports[`View Field Resolver - Failing Create Operation - v2 should fail to crea }, ], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -206,6 +211,7 @@ exports[`View Field Resolver - Failing Create Operation - v2 should fail to crea "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, diff --git a/packages/twenty-server/test/integration/graphql/suites/view/view-field/__snapshots__/object-identifier-update-side-effect-on-view-field.integration-spec.ts.snap b/packages/twenty-server/test/integration/graphql/suites/view/view-field/__snapshots__/object-identifier-update-side-effect-on-view-field.integration-spec.ts.snap index b23e961315..93eca0cf71 100644 --- a/packages/twenty-server/test/integration/graphql/suites/view/view-field/__snapshots__/object-identifier-update-side-effect-on-view-field.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/graphql/suites/view/view-field/__snapshots__/object-identifier-update-side-effect-on-view-field.integration-spec.ts.snap @@ -40,6 +40,7 @@ exports[`View Field Resolver - Successful object metadata identifier update side }, ], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -62,6 +63,7 @@ exports[`View Field Resolver - Successful object metadata identifier update side "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -112,6 +114,7 @@ exports[`View Field Resolver - Successful object metadata identifier update side }, ], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -134,6 +137,7 @@ exports[`View Field Resolver - Successful object metadata identifier update side "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -182,6 +186,7 @@ exports[`View Field Resolver - Successful object metadata identifier update side }, ], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -204,6 +209,7 @@ exports[`View Field Resolver - Successful object metadata identifier update side "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -254,6 +260,7 @@ exports[`View Field Resolver - Successful object metadata identifier update side }, ], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -276,6 +283,7 @@ exports[`View Field Resolver - Successful object metadata identifier update side "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -326,6 +334,7 @@ exports[`View Field Resolver - Successful object metadata identifier update side }, ], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -348,6 +357,7 @@ exports[`View Field Resolver - Successful object metadata identifier update side "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, diff --git a/packages/twenty-server/test/integration/graphql/suites/view/view-filter-group-resolver.integration-spec.ts b/packages/twenty-server/test/integration/graphql/suites/view/view-filter-group-resolver.integration-spec.ts index 26478ff6bb..99efb3cc73 100644 --- a/packages/twenty-server/test/integration/graphql/suites/view/view-filter-group-resolver.integration-spec.ts +++ b/packages/twenty-server/test/integration/graphql/suites/view/view-filter-group-resolver.integration-spec.ts @@ -456,7 +456,6 @@ describe('View Filter Group Resolver', () => { ErrorCode.NOT_FOUND, generateViewFilterGroupExceptionMessage( ViewFilterGroupExceptionMessageKey.VIEW_FILTER_GROUP_NOT_FOUND, - TEST_NOT_EXISTING_VIEW_FILTER_GROUP_ID, ), ); }); @@ -503,7 +502,6 @@ describe('View Filter Group Resolver', () => { ErrorCode.NOT_FOUND, generateViewFilterGroupExceptionMessage( ViewFilterGroupExceptionMessageKey.VIEW_FILTER_GROUP_NOT_FOUND, - TEST_NOT_EXISTING_VIEW_FILTER_GROUP_ID, ), ); }); @@ -543,7 +541,6 @@ describe('View Filter Group Resolver', () => { ErrorCode.NOT_FOUND, generateViewFilterGroupExceptionMessage( ViewFilterGroupExceptionMessageKey.VIEW_FILTER_GROUP_NOT_FOUND, - TEST_NOT_EXISTING_VIEW_FILTER_GROUP_ID, ), ); }); diff --git a/packages/twenty-server/test/integration/metadata/suites/agent/__snapshots__/failing-agent-creation.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/agent/__snapshots__/failing-agent-creation.integration-spec.ts.snap index 8626c26a01..ef79ff5ad7 100644 --- a/packages/twenty-server/test/integration/metadata/suites/agent/__snapshots__/failing-agent-creation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/metadata/suites/agent/__snapshots__/failing-agent-creation.integration-spec.ts.snap @@ -39,6 +39,7 @@ exports[`Agent creation should fail when computed name already exists 1`] = ` "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -61,6 +62,7 @@ exports[`Agent creation should fail when computed name already exists 1`] = ` "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -110,6 +112,7 @@ exports[`Agent creation should fail when label is empty string 1`] = ` "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -132,6 +135,7 @@ exports[`Agent creation should fail when label is empty string 1`] = ` "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -195,6 +199,7 @@ exports[`Agent creation should fail when modelId is empty string 1`] = ` "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -217,6 +222,7 @@ exports[`Agent creation should fail when modelId is empty string 1`] = ` "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -280,6 +286,7 @@ exports[`Agent creation should fail when prompt is empty string 1`] = ` "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -302,6 +309,7 @@ exports[`Agent creation should fail when prompt is empty string 1`] = ` "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -365,6 +373,7 @@ exports[`Agent creation should fail when responseFormat has invalid type 1`] = ` "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -387,6 +396,7 @@ exports[`Agent creation should fail when responseFormat has invalid type 1`] = ` "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -436,6 +446,7 @@ exports[`Agent creation should fail when responseFormat type is json but schema "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -458,6 +469,7 @@ exports[`Agent creation should fail when responseFormat type is json but schema "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -507,6 +519,7 @@ exports[`Agent creation should fail when responseFormat type is text but schema "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -529,6 +542,7 @@ exports[`Agent creation should fail when responseFormat type is text but schema "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, diff --git a/packages/twenty-server/test/integration/metadata/suites/agent/__snapshots__/failing-agent-deletion.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/agent/__snapshots__/failing-agent-deletion.integration-spec.ts.snap index c16bd492b2..0c86d38217 100644 --- a/packages/twenty-server/test/integration/metadata/suites/agent/__snapshots__/failing-agent-deletion.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/metadata/suites/agent/__snapshots__/failing-agent-deletion.integration-spec.ts.snap @@ -39,6 +39,7 @@ exports[`Agent deletion should fail should fail when attempting to delete a stan "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -61,6 +62,7 @@ exports[`Agent deletion should fail should fail when attempting to delete a stan "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, diff --git a/packages/twenty-server/test/integration/metadata/suites/agent/__snapshots__/failing-agent-update.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/agent/__snapshots__/failing-agent-update.integration-spec.ts.snap index f8d17f24e5..d390582611 100644 --- a/packages/twenty-server/test/integration/metadata/suites/agent/__snapshots__/failing-agent-update.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/metadata/suites/agent/__snapshots__/failing-agent-update.integration-spec.ts.snap @@ -38,6 +38,7 @@ exports[`Agent update should fail should fail when attempting to update a standa "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -60,6 +61,7 @@ exports[`Agent update should fail should fail when attempting to update a standa "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -130,6 +132,7 @@ exports[`Agent update should fail when settings null required properties 1`] = ` "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -152,6 +155,7 @@ exports[`Agent update should fail when settings null required properties 1`] = ` "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -200,6 +204,7 @@ exports[`Agent update should fail when updating label to one that already exists "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -222,6 +227,7 @@ exports[`Agent update should fail when updating label to one that already exists "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -270,6 +276,7 @@ exports[`Agent update should fail when updating responseFormat type to json with "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -292,6 +299,7 @@ exports[`Agent update should fail when updating responseFormat type to json with "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -340,6 +348,7 @@ exports[`Agent update should fail when updating responseFormat type to text with "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -362,6 +371,7 @@ exports[`Agent update should fail when updating responseFormat type to text with "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -410,6 +420,7 @@ exports[`Agent update should fail when updating responseFormat with invalid type "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -432,6 +443,7 @@ exports[`Agent update should fail when updating responseFormat with invalid type "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -480,6 +492,7 @@ exports[`Agent update should fail when updating with empty label 1`] = ` "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -502,6 +515,7 @@ exports[`Agent update should fail when updating with empty label 1`] = ` "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -550,6 +564,7 @@ exports[`Agent update should fail when updating with empty modelId 1`] = ` "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -572,6 +587,7 @@ exports[`Agent update should fail when updating with empty modelId 1`] = ` "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -620,6 +636,7 @@ exports[`Agent update should fail when updating with empty prompt 1`] = ` "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -642,6 +659,7 @@ exports[`Agent update should fail when updating with empty prompt 1`] = ` "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/__snapshots__/create-one-field-metadata.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/field-metadata/__snapshots__/create-one-field-metadata.integration-spec.ts.snap index f0823a7220..2970ea3948 100644 --- a/packages/twenty-server/test/integration/metadata/suites/field-metadata/__snapshots__/create-one-field-metadata.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/metadata/suites/field-metadata/__snapshots__/create-one-field-metadata.integration-spec.ts.snap @@ -41,6 +41,7 @@ exports[`createOne FieldMetadataService name/label sync should return an error w "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -63,6 +64,7 @@ exports[`createOne FieldMetadataService name/label sync should return an error w "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/__snapshots__/failing-create-one-field-metadata.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/field-metadata/__snapshots__/failing-create-one-field-metadata.integration-spec.ts.snap index 9b5f133c66..df92625013 100644 --- a/packages/twenty-server/test/integration/metadata/suites/field-metadata/__snapshots__/failing-create-one-field-metadata.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/metadata/suites/field-metadata/__snapshots__/failing-create-one-field-metadata.integration-spec.ts.snap @@ -42,6 +42,7 @@ exports[`Failing create field metadata tests suite should fail to create NUMERIC "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -64,6 +65,7 @@ exports[`Failing create field metadata tests suite should fail to create NUMERIC "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -117,6 +119,7 @@ exports[`Failing create field metadata tests suite should fail to create POSITIO "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -139,6 +142,7 @@ exports[`Failing create field metadata tests suite should fail to create POSITIO "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -185,6 +189,7 @@ exports[`Failing create field metadata tests suite should fail to create TS_VECT "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -207,6 +212,7 @@ exports[`Failing create field metadata tests suite should fail to create TS_VECT "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/__snapshots__/update-one-field-metadata.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/field-metadata/__snapshots__/update-one-field-metadata.integration-spec.ts.snap index 139c700e05..f36ec396c6 100644 --- a/packages/twenty-server/test/integration/metadata/suites/field-metadata/__snapshots__/update-one-field-metadata.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/metadata/suites/field-metadata/__snapshots__/update-one-field-metadata.integration-spec.ts.snap @@ -41,6 +41,7 @@ exports[`updateOne failing update should not update a field name if it is not sy "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -63,6 +64,7 @@ exports[`updateOne failing update should not update a field name if it is not sy "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -121,6 +123,7 @@ exports[`updateOne failing update should throw if the field name is not availabl "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -143,6 +146,7 @@ exports[`updateOne failing update should throw if the field name is not availabl "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/enum/__snapshots__/failing-create-one-field-metadata-enum-v2.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/field-metadata/enum/__snapshots__/failing-create-one-field-metadata-enum-v2.integration-spec.ts.snap index 5d4efc58fb..31c5d84161 100644 --- a/packages/twenty-server/test/integration/metadata/suites/field-metadata/enum/__snapshots__/failing-create-one-field-metadata-enum-v2.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/metadata/suites/field-metadata/enum/__snapshots__/failing-create-one-field-metadata-enum-v2.integration-spec.ts.snap @@ -42,6 +42,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -64,6 +65,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -117,6 +119,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -139,6 +142,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -192,6 +196,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -214,6 +219,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -282,6 +288,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -304,6 +311,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -372,6 +380,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -394,6 +403,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -462,6 +472,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -484,6 +495,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -552,6 +564,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -574,6 +587,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -627,6 +641,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -649,6 +664,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -702,6 +718,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -724,6 +741,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -783,6 +801,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -805,6 +824,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -858,6 +878,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -880,6 +901,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -933,6 +955,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -955,6 +978,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1008,6 +1032,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1030,6 +1055,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1101,6 +1127,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1123,6 +1150,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1176,6 +1204,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1198,6 +1227,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1251,6 +1281,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1273,6 +1304,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1326,6 +1358,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1348,6 +1381,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1401,6 +1435,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1423,6 +1458,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1476,6 +1512,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1498,6 +1535,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1551,6 +1589,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1573,6 +1612,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1626,6 +1666,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1648,6 +1689,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1706,6 +1748,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1728,6 +1771,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1780,6 +1824,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1802,6 +1847,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1855,6 +1901,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1877,6 +1924,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1929,6 +1977,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1951,6 +2000,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -2010,6 +2060,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -2032,6 +2083,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -2085,6 +2137,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -2107,6 +2160,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -2160,6 +2214,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -2182,6 +2237,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -2235,6 +2291,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -2257,6 +2314,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -2316,6 +2374,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -2338,6 +2397,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -2391,6 +2451,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -2413,6 +2474,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -2466,6 +2528,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -2488,6 +2551,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -2547,6 +2611,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -2569,6 +2634,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -2621,6 +2687,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -2643,6 +2710,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -2695,6 +2763,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -2717,6 +2786,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -2770,6 +2840,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -2792,6 +2863,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -2845,6 +2917,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -2867,6 +2940,7 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -2926,6 +3000,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -2948,6 +3023,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -3001,6 +3077,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -3023,6 +3100,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -3076,6 +3154,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -3098,6 +3177,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -3166,6 +3246,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -3188,6 +3269,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -3256,6 +3338,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -3278,6 +3361,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -3346,6 +3430,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -3368,6 +3453,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -3436,6 +3522,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -3458,6 +3545,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -3511,6 +3599,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -3533,6 +3622,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -3592,6 +3682,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -3614,6 +3705,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -3667,6 +3759,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -3689,6 +3782,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -3742,6 +3836,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -3764,6 +3859,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -3817,6 +3913,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -3839,6 +3936,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -3892,6 +3990,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -3914,6 +4013,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -3967,6 +4067,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -3989,6 +4090,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -4042,6 +4144,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -4064,6 +4167,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -4117,6 +4221,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -4139,6 +4244,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -4192,6 +4298,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -4214,6 +4321,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -4267,6 +4375,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -4289,6 +4398,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -4347,6 +4457,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -4369,6 +4480,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -4421,6 +4533,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -4443,6 +4556,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -4496,6 +4610,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -4518,6 +4633,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -4570,6 +4686,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -4592,6 +4709,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -4651,6 +4769,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -4673,6 +4792,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -4726,6 +4846,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -4748,6 +4869,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -4801,6 +4923,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -4823,6 +4946,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -4876,6 +5000,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -4898,6 +5023,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -4957,6 +5083,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -4979,6 +5106,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -5032,6 +5160,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -5054,6 +5183,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -5107,6 +5237,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -5129,6 +5260,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -5188,6 +5320,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -5210,6 +5343,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -5262,6 +5396,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -5284,6 +5419,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -5336,6 +5472,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -5358,6 +5495,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -5411,6 +5549,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -5433,6 +5572,7 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/enum/__snapshots__/failing-update-one-enum-field-metadata-v2.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/field-metadata/enum/__snapshots__/failing-update-one-enum-field-metadata-v2.integration-spec.ts.snap index e7a15cf7ee..0f7c8888b5 100644 --- a/packages/twenty-server/test/integration/metadata/suites/field-metadata/enum/__snapshots__/failing-update-one-enum-field-metadata-v2.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/metadata/suites/field-metadata/enum/__snapshots__/failing-update-one-enum-field-metadata-v2.integration-spec.ts.snap @@ -42,6 +42,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -64,6 +65,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -117,6 +119,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -139,6 +142,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -192,6 +196,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -214,6 +219,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -282,6 +288,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -304,6 +311,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -372,6 +380,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -394,6 +403,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -462,6 +472,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -484,6 +495,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -552,6 +564,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -574,6 +587,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -627,6 +641,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -649,6 +664,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -702,6 +718,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -724,6 +741,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -783,6 +801,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -805,6 +824,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -858,6 +878,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -880,6 +901,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -933,6 +955,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -955,6 +978,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1008,6 +1032,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1030,6 +1055,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1101,6 +1127,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1123,6 +1150,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1176,6 +1204,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1198,6 +1227,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1251,6 +1281,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1273,6 +1304,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1326,6 +1358,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1348,6 +1381,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1401,6 +1435,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1423,6 +1458,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1476,6 +1512,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1498,6 +1535,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1551,6 +1589,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1573,6 +1612,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1626,6 +1666,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1648,6 +1689,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1706,6 +1748,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1728,6 +1771,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1780,6 +1824,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1802,6 +1847,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1854,6 +1900,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1876,6 +1923,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1935,6 +1983,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1957,6 +2006,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -2010,6 +2060,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -2032,6 +2083,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -2085,6 +2137,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -2107,6 +2160,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -2160,6 +2214,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -2182,6 +2237,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -2241,6 +2297,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -2263,6 +2320,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -2316,6 +2374,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -2338,6 +2397,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -2391,6 +2451,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -2413,6 +2474,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -2472,6 +2534,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -2494,6 +2557,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -2546,6 +2610,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -2568,6 +2633,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -2620,6 +2686,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -2642,6 +2709,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -2695,6 +2763,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -2717,6 +2786,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -2770,6 +2840,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -2792,6 +2863,7 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -2851,6 +2923,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -2873,6 +2946,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -2926,6 +3000,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -2948,6 +3023,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -3001,6 +3077,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -3023,6 +3100,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -3091,6 +3169,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -3113,6 +3192,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -3181,6 +3261,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -3203,6 +3284,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -3271,6 +3353,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -3293,6 +3376,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -3361,6 +3445,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -3383,6 +3468,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -3436,6 +3522,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -3458,6 +3545,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -3517,6 +3605,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -3539,6 +3628,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -3592,6 +3682,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -3614,6 +3705,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -3667,6 +3759,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -3689,6 +3782,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -3742,6 +3836,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -3764,6 +3859,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -3817,6 +3913,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -3839,6 +3936,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -3892,6 +3990,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -3914,6 +4013,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -3967,6 +4067,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -3989,6 +4090,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -4042,6 +4144,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -4064,6 +4167,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -4117,6 +4221,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -4139,6 +4244,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -4192,6 +4298,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -4214,6 +4321,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -4272,6 +4380,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -4294,6 +4403,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -4346,6 +4456,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -4368,6 +4479,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -4420,6 +4532,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -4442,6 +4555,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -4501,6 +4615,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -4523,6 +4638,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -4576,6 +4692,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -4598,6 +4715,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -4651,6 +4769,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -4673,6 +4792,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -4726,6 +4846,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -4748,6 +4869,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -4807,6 +4929,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -4829,6 +4952,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -4882,6 +5006,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -4904,6 +5029,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -4957,6 +5083,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -4979,6 +5106,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -5038,6 +5166,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -5060,6 +5189,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -5112,6 +5242,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -5134,6 +5265,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -5186,6 +5318,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -5208,6 +5341,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -5261,6 +5395,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -5283,6 +5418,7 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/morph-relation/__snapshots__/failing-add-one-target-to-metadata-morph-relation-v2.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/field-metadata/morph-relation/__snapshots__/failing-add-one-target-to-metadata-morph-relation-v2.integration-spec.ts.snap index a91a711c67..ce6dbcd975 100644 --- a/packages/twenty-server/test/integration/metadata/suites/field-metadata/morph-relation/__snapshots__/failing-add-one-target-to-metadata-morph-relation-v2.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/metadata/suites/field-metadata/morph-relation/__snapshots__/failing-add-one-target-to-metadata-morph-relation-v2.integration-spec.ts.snap @@ -84,6 +84,7 @@ exports[`updateOne FieldMetadataService morph relation fields v2 - Add one targe "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -106,6 +107,7 @@ exports[`updateOne FieldMetadataService morph relation fields v2 - Add one targe "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -188,6 +190,7 @@ exports[`updateOne FieldMetadataService morph relation fields v2 - Add one targe "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -210,6 +213,7 @@ exports[`updateOne FieldMetadataService morph relation fields v2 - Add one targe "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/morph-relation/__snapshots__/failing-create-one-field-metadata-morph-relation-v2.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/field-metadata/morph-relation/__snapshots__/failing-create-one-field-metadata-morph-relation-v2.integration-spec.ts.snap index e60735edca..e40610b6c8 100644 --- a/packages/twenty-server/test/integration/metadata/suites/field-metadata/morph-relation/__snapshots__/failing-create-one-field-metadata-morph-relation-v2.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/metadata/suites/field-metadata/morph-relation/__snapshots__/failing-create-one-field-metadata-morph-relation-v2.integration-spec.ts.snap @@ -89,6 +89,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 Morh re "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -111,6 +112,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 Morh re "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -162,6 +164,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 Morh re "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -184,6 +187,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 Morh re "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -229,6 +233,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -251,6 +256,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -296,6 +302,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -318,6 +325,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -391,6 +399,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -413,6 +422,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -480,6 +490,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -502,6 +513,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -569,6 +581,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -591,6 +604,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -664,6 +678,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -686,6 +701,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -739,6 +755,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -761,6 +778,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -814,6 +832,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -836,6 +855,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -888,6 +908,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -910,6 +931,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -955,6 +977,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -977,6 +1000,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1022,6 +1046,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1044,6 +1069,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1089,6 +1115,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1111,6 +1138,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1159,6 +1187,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1181,6 +1210,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1227,6 +1257,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1249,6 +1280,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1294,6 +1326,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1316,6 +1349,7 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/relation/__snapshots__/failing-field-metadata-relation-creation.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/field-metadata/relation/__snapshots__/failing-field-metadata-relation-creation.integration-spec.ts.snap index fdfe754e11..8ebe43e377 100644 --- a/packages/twenty-server/test/integration/metadata/suites/field-metadata/relation/__snapshots__/failing-field-metadata-relation-creation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/metadata/suites/field-metadata/relation/__snapshots__/failing-field-metadata-relation-creation.integration-spec.ts.snap @@ -41,6 +41,7 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE (rela "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -63,6 +64,7 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE (rela "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -114,6 +116,7 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE (rela "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -136,6 +139,7 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE (rela "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -193,6 +197,7 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE (rela "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -215,6 +220,7 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE (rela "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -266,6 +272,7 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE (rela "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -288,6 +295,7 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE (rela "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -345,6 +353,7 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE (rela "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -367,6 +376,7 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE (rela "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -418,6 +428,7 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE (rela "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -440,6 +451,7 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE (rela "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -491,6 +503,7 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE (rela "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -513,6 +526,7 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE (rela "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -564,6 +578,7 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE (rela "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -586,6 +601,7 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE (rela "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -636,6 +652,7 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE (rela "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -658,6 +675,7 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE (rela "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -741,6 +759,7 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE when "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -763,6 +782,7 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE when "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -814,6 +834,7 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE when "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -836,6 +857,7 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE when "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -887,6 +909,7 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE when "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -909,6 +932,7 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE when "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -992,6 +1016,7 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE when "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1014,6 +1039,7 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE when "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1065,6 +1091,7 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE when "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1087,6 +1114,7 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE when "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1155,6 +1183,7 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY (rela "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1177,6 +1206,7 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY (rela "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1246,6 +1276,7 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY (rela "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1268,6 +1299,7 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY (rela "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1343,6 +1375,7 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY (rela "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1365,6 +1398,7 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY (rela "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1434,6 +1468,7 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY (rela "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1456,6 +1491,7 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY (rela "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1531,6 +1567,7 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY (rela "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1553,6 +1590,7 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY (rela "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1606,6 +1644,7 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY (rela "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1628,6 +1667,7 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY (rela "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1681,6 +1721,7 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY (rela "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1703,6 +1744,7 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY (rela "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1756,6 +1798,7 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY (rela "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1778,6 +1821,7 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY (rela "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1830,6 +1874,7 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY (rela "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1852,6 +1897,7 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY (rela "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1937,6 +1983,7 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY when "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1959,6 +2006,7 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY when "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -2012,6 +2060,7 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY when "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -2034,6 +2083,7 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY when "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -2087,6 +2137,7 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY when "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -2109,6 +2160,7 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY when "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -2194,6 +2246,7 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY when "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -2216,6 +2269,7 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY when "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -2269,6 +2323,7 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY when "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -2291,6 +2346,7 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY when "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -2343,6 +2399,7 @@ exports[`Field metadata relation creation should fail should fail when creating "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -2365,6 +2422,7 @@ exports[`Field metadata relation creation should fail should fail when creating "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -2432,6 +2490,7 @@ exports[`Field metadata relation creation should fail should fail when creating "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -2454,6 +2513,7 @@ exports[`Field metadata relation creation should fail should fail when creating "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -2537,6 +2597,7 @@ exports[`Field metadata relation creation should fail should fail when creating "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -2559,6 +2620,7 @@ exports[`Field metadata relation creation should fail should fail when creating "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -2642,6 +2704,7 @@ exports[`Field metadata relation creation should fail should fail when creating "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -2664,6 +2727,7 @@ exports[`Field metadata relation creation should fail should fail when creating "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/relation/__snapshots__/failing-field-metadata-relation-update.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/field-metadata/relation/__snapshots__/failing-field-metadata-relation-update.integration-spec.ts.snap index cf4f4278fd..7634b2507d 100644 --- a/packages/twenty-server/test/integration/metadata/suites/field-metadata/relation/__snapshots__/failing-field-metadata-relation-update.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/metadata/suites/field-metadata/relation/__snapshots__/failing-field-metadata-relation-update.integration-spec.ts.snap @@ -48,6 +48,7 @@ exports[`Field metadata relation update should fail relation when name is not in "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -70,6 +71,7 @@ exports[`Field metadata relation update should fail relation when name is not in "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/unique-field/__snapshots__/create-one-unique-field-metadata.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/field-metadata/unique-field/__snapshots__/create-one-unique-field-metadata.integration-spec.ts.snap index ede7b1ee65..28f9f98f3d 100644 --- a/packages/twenty-server/test/integration/metadata/suites/field-metadata/unique-field/__snapshots__/create-one-unique-field-metadata.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/metadata/suites/field-metadata/unique-field/__snapshots__/create-one-unique-field-metadata.integration-spec.ts.snap @@ -39,6 +39,7 @@ exports[`failing createOne unique field metadata should fail for composite type "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -61,6 +62,7 @@ exports[`failing createOne unique field metadata should fail for composite type "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -110,6 +112,7 @@ exports[`failing createOne unique field metadata should fail when field has cust "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -132,6 +135,7 @@ exports[`failing createOne unique field metadata should fail when field has cust "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, diff --git a/packages/twenty-server/test/integration/metadata/suites/object-metadata/__snapshots__/failing-create-one-object-metadata-v2.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/object-metadata/__snapshots__/failing-create-one-object-metadata-v2.integration-spec.ts.snap index 9410eff6cf..c276bcb5b1 100644 --- a/packages/twenty-server/test/integration/metadata/suites/object-metadata/__snapshots__/failing-create-one-object-metadata-v2.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/metadata/suites/object-metadata/__snapshots__/failing-create-one-object-metadata-v2.integration-spec.ts.snap @@ -635,6 +635,7 @@ exports[`Object metadata creation should fail v2 when labelPlural contains only }, ], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -657,6 +658,7 @@ exports[`Object metadata creation should fail v2 when labelPlural contains only "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1302,6 +1304,7 @@ exports[`Object metadata creation should fail v2 when labelPlural exceeds maximu }, ], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -1324,6 +1327,7 @@ exports[`Object metadata creation should fail v2 when labelPlural exceeds maximu "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -1980,6 +1984,7 @@ exports[`Object metadata creation should fail v2 when labelSingular contains onl }, ], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -2002,6 +2007,7 @@ exports[`Object metadata creation should fail v2 when labelSingular contains onl "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -2647,6 +2653,7 @@ exports[`Object metadata creation should fail v2 when labelSingular exceeds maxi }, ], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -2669,6 +2676,7 @@ exports[`Object metadata creation should fail v2 when labelSingular exceeds maxi "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -3325,6 +3333,7 @@ exports[`Object metadata creation should fail v2 when labels are identical 1`] = }, ], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -3347,6 +3356,7 @@ exports[`Object metadata creation should fail v2 when labels are identical 1`] = "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -3992,6 +4002,7 @@ exports[`Object metadata creation should fail v2 when labels with whitespaces re }, ], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -4014,6 +4025,7 @@ exports[`Object metadata creation should fail v2 when labels with whitespaces re "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -4689,6 +4701,7 @@ exports[`Object metadata creation should fail v2 when name exceeds maximum lengt }, ], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -4711,6 +4724,7 @@ exports[`Object metadata creation should fail v2 when name exceeds maximum lengt "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -5356,6 +5370,7 @@ exports[`Object metadata creation should fail v2 when namePlural has invalid cha }, ], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -5378,6 +5393,7 @@ exports[`Object metadata creation should fail v2 when namePlural has invalid cha "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -6023,6 +6039,7 @@ exports[`Object metadata creation should fail v2 when namePlural is a reserved k }, ], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -6045,6 +6062,7 @@ exports[`Object metadata creation should fail v2 when namePlural is a reserved k "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -6707,6 +6725,7 @@ exports[`Object metadata creation should fail v2 when namePlural is not camelCas }, ], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -6729,6 +6748,7 @@ exports[`Object metadata creation should fail v2 when namePlural is not camelCas "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -7440,6 +7460,7 @@ exports[`Object metadata creation should fail v2 when nameSingular contains only }, ], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -7462,6 +7483,7 @@ exports[`Object metadata creation should fail v2 when nameSingular contains only "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -8161,6 +8183,7 @@ exports[`Object metadata creation should fail v2 when nameSingular contains only }, ], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -8183,6 +8206,7 @@ exports[`Object metadata creation should fail v2 when nameSingular contains only "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -8864,6 +8888,7 @@ exports[`Object metadata creation should fail v2 when nameSingular has invalid c }, ], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -8886,6 +8911,7 @@ exports[`Object metadata creation should fail v2 when nameSingular has invalid c "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -9555,6 +9581,7 @@ exports[`Object metadata creation should fail v2 when nameSingular is a reserved }, ], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -9577,6 +9604,7 @@ exports[`Object metadata creation should fail v2 when nameSingular is a reserved "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -10299,6 +10327,7 @@ exports[`Object metadata creation should fail v2 when nameSingular is not camelC }, ], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -10321,6 +10350,7 @@ exports[`Object metadata creation should fail v2 when nameSingular is not camelC "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -10966,6 +10996,7 @@ exports[`Object metadata creation should fail v2 when names are identical 1`] = }, ], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -10988,6 +11019,7 @@ exports[`Object metadata creation should fail v2 when names are identical 1`] = "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -11633,6 +11665,7 @@ exports[`Object metadata creation should fail v2 when names with whitespaces res }, ], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -11655,6 +11688,7 @@ exports[`Object metadata creation should fail v2 when names with whitespaces res "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, diff --git a/packages/twenty-server/test/integration/metadata/suites/object-metadata/__snapshots__/failing-update-one-object-metadata.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/object-metadata/__snapshots__/failing-update-one-object-metadata.integration-spec.ts.snap index 0fd1bee333..b66c38112f 100644 --- a/packages/twenty-server/test/integration/metadata/suites/object-metadata/__snapshots__/failing-update-one-object-metadata.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/metadata/suites/object-metadata/__snapshots__/failing-update-one-object-metadata.integration-spec.ts.snap @@ -58,6 +58,7 @@ exports[`Object metadata update should fail when labelIdentifier is a UUID field }, ], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -80,6 +81,7 @@ exports[`Object metadata update should fail when labelIdentifier is a UUID field "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -149,6 +151,7 @@ exports[`Object metadata update should fail when labelIdentifier is not a TEXT o }, ], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -171,6 +174,7 @@ exports[`Object metadata update should fail when labelIdentifier is not a TEXT o "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -245,6 +249,7 @@ exports[`Object metadata update should fail when labelIdentifier is not a known }, ], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -267,6 +272,7 @@ exports[`Object metadata update should fail when labelIdentifier is not a known "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -336,6 +342,7 @@ exports[`Object metadata update should fail when labelIdentifier is null 1`] = ` "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -358,6 +365,7 @@ exports[`Object metadata update should fail when labelIdentifier is null 1`] = ` "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, diff --git a/packages/twenty-server/test/integration/metadata/suites/page-layout-tab/__snapshots__/failing-page-layout-tab-creation.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/page-layout-tab/__snapshots__/failing-page-layout-tab-creation.integration-spec.ts.snap index 021a43b742..b064615513 100644 --- a/packages/twenty-server/test/integration/metadata/suites/page-layout-tab/__snapshots__/failing-page-layout-tab-creation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/metadata/suites/page-layout-tab/__snapshots__/failing-page-layout-tab-creation.integration-spec.ts.snap @@ -39,6 +39,7 @@ exports[`Page layout tab creation should fail when pageLayoutId references non-e "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -61,6 +62,7 @@ exports[`Page layout tab creation should fail when pageLayoutId references non-e "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, diff --git a/packages/twenty-server/test/integration/metadata/suites/page-layout-widget/__snapshots__/failing-page-layout-widget-creation.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/page-layout-widget/__snapshots__/failing-page-layout-widget-creation.integration-spec.ts.snap index 138ba1b5e8..41beb7d5a1 100644 --- a/packages/twenty-server/test/integration/metadata/suites/page-layout-widget/__snapshots__/failing-page-layout-widget-creation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/metadata/suites/page-layout-widget/__snapshots__/failing-page-layout-widget-creation.integration-spec.ts.snap @@ -50,6 +50,7 @@ exports[`Page layout widget creation should fail when pageLayoutTabId references "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -72,6 +73,7 @@ exports[`Page layout widget creation should fail when pageLayoutTabId references "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, diff --git a/packages/twenty-server/test/integration/metadata/suites/role/__snapshots__/failing-role-creation.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/role/__snapshots__/failing-role-creation.integration-spec.ts.snap index f5b1659fa2..bf587fe33a 100644 --- a/packages/twenty-server/test/integration/metadata/suites/role/__snapshots__/failing-role-creation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/metadata/suites/role/__snapshots__/failing-role-creation.integration-spec.ts.snap @@ -39,6 +39,7 @@ exports[`Role creation should fail when canReadAllObjectRecords is false but can "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -61,6 +62,7 @@ exports[`Role creation should fail when canReadAllObjectRecords is false but can "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -110,6 +112,7 @@ exports[`Role creation should fail when canReadAllObjectRecords is false but can "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -132,6 +135,7 @@ exports[`Role creation should fail when canReadAllObjectRecords is false but can "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -181,6 +185,7 @@ exports[`Role creation should fail when canReadAllObjectRecords is false but can "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -203,6 +208,7 @@ exports[`Role creation should fail when canReadAllObjectRecords is false but can "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -252,6 +258,7 @@ exports[`Role creation should fail when canReadAllObjectRecords is false but mul "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -274,6 +281,7 @@ exports[`Role creation should fail when canReadAllObjectRecords is false but mul "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -323,6 +331,7 @@ exports[`Role creation should fail when label already exists 1`] = ` "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -345,6 +354,7 @@ exports[`Role creation should fail when label already exists 1`] = ` "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, diff --git a/packages/twenty-server/test/integration/metadata/suites/role/__snapshots__/failing-role-update.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/role/__snapshots__/failing-role-update.integration-spec.ts.snap index 446f1876ef..7f2f174143 100644 --- a/packages/twenty-server/test/integration/metadata/suites/role/__snapshots__/failing-role-update.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/metadata/suites/role/__snapshots__/failing-role-update.integration-spec.ts.snap @@ -50,6 +50,7 @@ exports[`Role update should fail updating role with existing write permissions s "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -72,6 +73,7 @@ exports[`Role update should fail updating role with existing write permissions s "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -120,6 +122,7 @@ exports[`Role update should fail when updating a non-editable system role 1`] = "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -142,6 +145,7 @@ exports[`Role update should fail when updating a non-editable system role 1`] = "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -190,6 +194,7 @@ exports[`Role update should fail when updating canReadAllObjectRecords to false "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -212,6 +217,7 @@ exports[`Role update should fail when updating canReadAllObjectRecords to false "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -260,6 +266,7 @@ exports[`Role update should fail when updating canReadAllObjectRecords to false "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -282,6 +289,7 @@ exports[`Role update should fail when updating canReadAllObjectRecords to false "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -330,6 +338,7 @@ exports[`Role update should fail when updating canReadAllObjectRecords to false "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -352,6 +361,7 @@ exports[`Role update should fail when updating canReadAllObjectRecords to false "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -400,6 +410,7 @@ exports[`Role update should fail when updating label to one that already exists "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -422,6 +433,7 @@ exports[`Role update should fail when updating label to one that already exists "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, @@ -470,6 +482,7 @@ exports[`Role update should fail when updating to enable write permissions witho "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [], }, "message": "Validation failed for 0 object(s) and 0 field(s)", @@ -492,6 +505,7 @@ exports[`Role update should fail when updating to enable write permissions witho "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, diff --git a/packages/twenty-server/test/integration/metadata/suites/view-filter-group/__snapshots__/failing-view-filter-group-creation-circular-dependency.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/__snapshots__/failing-view-filter-group-creation-circular-dependency.integration-spec.ts.snap new file mode 100644 index 0000000000..36eaaf5b94 --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/__snapshots__/failing-view-filter-group-creation-circular-dependency.integration-spec.ts.snap @@ -0,0 +1,150 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`View Filter Group creation should fail with circular dependency when id equals parentViewFilterGroupId (self-reference) 1`] = ` +{ + "extensions": { + "code": "METADATA_VALIDATION_FAILED", + "errors": { + "agent": [], + "cronTrigger": [], + "databaseEventTrigger": [], + "fieldMetadata": [], + "index": [], + "objectMetadata": [], + "pageLayout": [], + "pageLayoutTab": [], + "pageLayoutWidget": [], + "role": [], + "roleTarget": [], + "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], + "serverlessFunction": [], + "view": [], + "viewField": [], + "viewFilter": [], + "viewFilterGroup": [ + { + "errors": [ + { + "code": "CIRCULAR_DEPENDENCY", + "message": "View filter group cannot be its own parent", + "userFriendlyMessage": "View filter group cannot be its own parent", + }, + { + "code": "VIEW_FILTER_GROUP_NOT_FOUND", + "message": "Parent view filter group not found", + "userFriendlyMessage": "Parent view filter group not found", + }, + ], + "flatEntityMinimalInformation": { + "id": Any, + }, + "status": "fail", + "type": "create_view_filter_group", + }, + ], + "viewGroup": [], + }, + "message": "Validation failed for 0 object(s) and 0 field(s)", + "summary": { + "invalidAgent": 0, + "invalidCronTrigger": 0, + "invalidDatabaseEventTrigger": 0, + "invalidFieldMetadata": 0, + "invalidIndex": 0, + "invalidObjectMetadata": 0, + "invalidPageLayout": 0, + "invalidPageLayoutTab": 0, + "invalidPageLayoutWidget": 0, + "invalidRole": 0, + "invalidRoleTarget": 0, + "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, + "invalidServerlessFunction": 0, + "invalidView": 0, + "invalidViewField": 0, + "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, + "invalidViewGroup": 0, + "totalErrors": 0, + }, + "userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)", + }, + "message": "Multiple validation errors occurred while creating view filter group", + "name": "GraphQLError", +} +`; + +exports[`View Filter Group creation should fail with circular dependency when max depth is exceeded during creation 1`] = ` +{ + "extensions": { + "code": "METADATA_VALIDATION_FAILED", + "errors": { + "agent": [], + "cronTrigger": [], + "databaseEventTrigger": [], + "fieldMetadata": [], + "index": [], + "objectMetadata": [], + "pageLayout": [], + "pageLayoutTab": [], + "pageLayoutWidget": [], + "role": [], + "roleTarget": [], + "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], + "serverlessFunction": [], + "view": [], + "viewField": [], + "viewFilter": [], + "viewFilterGroup": [ + { + "errors": [ + { + "code": "MAX_DEPTH_EXCEEDED", + "message": "View filter group hierarchy exceeds maximum depth of 2", + "userFriendlyMessage": "View filter group hierarchy exceeds maximum depth of 2", + }, + ], + "flatEntityMinimalInformation": { + "id": Any, + }, + "status": "fail", + "type": "create_view_filter_group", + }, + ], + "viewGroup": [], + }, + "message": "Validation failed for 0 object(s) and 0 field(s)", + "summary": { + "invalidAgent": 0, + "invalidCronTrigger": 0, + "invalidDatabaseEventTrigger": 0, + "invalidFieldMetadata": 0, + "invalidIndex": 0, + "invalidObjectMetadata": 0, + "invalidPageLayout": 0, + "invalidPageLayoutTab": 0, + "invalidPageLayoutWidget": 0, + "invalidRole": 0, + "invalidRoleTarget": 0, + "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, + "invalidServerlessFunction": 0, + "invalidView": 0, + "invalidViewField": 0, + "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, + "invalidViewGroup": 0, + "totalErrors": 0, + }, + "userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)", + }, + "message": "Multiple validation errors occurred while creating view filter group", + "name": "GraphQLError", +} +`; diff --git a/packages/twenty-server/test/integration/metadata/suites/view-filter-group/__snapshots__/failing-view-filter-group-creation.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/__snapshots__/failing-view-filter-group-creation.integration-spec.ts.snap new file mode 100644 index 0000000000..3b6cdde533 --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/__snapshots__/failing-view-filter-group-creation.integration-spec.ts.snap @@ -0,0 +1,237 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`View Filter Group creation should fail when parentViewFilterGroupId does not exist 1`] = ` +{ + "extensions": { + "code": "METADATA_VALIDATION_FAILED", + "errors": { + "agent": [], + "cronTrigger": [], + "databaseEventTrigger": [], + "fieldMetadata": [], + "index": [], + "objectMetadata": [], + "pageLayout": [], + "pageLayoutTab": [], + "pageLayoutWidget": [], + "role": [], + "roleTarget": [], + "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], + "serverlessFunction": [], + "view": [], + "viewField": [], + "viewFilter": [], + "viewFilterGroup": [ + { + "errors": [ + { + "code": "VIEW_FILTER_GROUP_NOT_FOUND", + "message": "Parent view filter group not found", + "userFriendlyMessage": "Parent view filter group not found", + }, + ], + "flatEntityMinimalInformation": { + "id": Any, + }, + "status": "fail", + "type": "create_view_filter_group", + }, + ], + "viewGroup": [], + }, + "message": "Validation failed for 0 object(s) and 0 field(s)", + "summary": { + "invalidAgent": 0, + "invalidCronTrigger": 0, + "invalidDatabaseEventTrigger": 0, + "invalidFieldMetadata": 0, + "invalidIndex": 0, + "invalidObjectMetadata": 0, + "invalidPageLayout": 0, + "invalidPageLayoutTab": 0, + "invalidPageLayoutWidget": 0, + "invalidRole": 0, + "invalidRoleTarget": 0, + "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, + "invalidServerlessFunction": 0, + "invalidView": 0, + "invalidViewField": 0, + "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, + "invalidViewGroup": 0, + "totalErrors": 0, + }, + "userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)", + }, + "message": "Multiple validation errors occurred while creating view filter group", + "name": "GraphQLError", +} +`; + +exports[`View Filter Group creation should fail when parentViewFilterGroupId equals id (self-reference) 1`] = ` +{ + "extensions": { + "code": "METADATA_VALIDATION_FAILED", + "errors": { + "agent": [], + "cronTrigger": [], + "databaseEventTrigger": [], + "fieldMetadata": [], + "index": [], + "objectMetadata": [], + "pageLayout": [], + "pageLayoutTab": [], + "pageLayoutWidget": [], + "role": [], + "roleTarget": [], + "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], + "serverlessFunction": [], + "view": [], + "viewField": [], + "viewFilter": [], + "viewFilterGroup": [ + { + "errors": [ + { + "code": "CIRCULAR_DEPENDENCY", + "message": "View filter group cannot be its own parent", + "userFriendlyMessage": "View filter group cannot be its own parent", + }, + { + "code": "VIEW_FILTER_GROUP_NOT_FOUND", + "message": "Parent view filter group not found", + "userFriendlyMessage": "Parent view filter group not found", + }, + ], + "flatEntityMinimalInformation": { + "id": Any, + }, + "status": "fail", + "type": "create_view_filter_group", + }, + ], + "viewGroup": [], + }, + "message": "Validation failed for 0 object(s) and 0 field(s)", + "summary": { + "invalidAgent": 0, + "invalidCronTrigger": 0, + "invalidDatabaseEventTrigger": 0, + "invalidFieldMetadata": 0, + "invalidIndex": 0, + "invalidObjectMetadata": 0, + "invalidPageLayout": 0, + "invalidPageLayoutTab": 0, + "invalidPageLayoutWidget": 0, + "invalidRole": 0, + "invalidRoleTarget": 0, + "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, + "invalidServerlessFunction": 0, + "invalidView": 0, + "invalidViewField": 0, + "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, + "invalidViewGroup": 0, + "totalErrors": 0, + }, + "userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)", + }, + "message": "Multiple validation errors occurred while creating view filter group", + "name": "GraphQLError", +} +`; + +exports[`View Filter Group creation should fail when viewId does not exist 1`] = ` +{ + "extensions": { + "code": "METADATA_VALIDATION_FAILED", + "errors": { + "agent": [], + "cronTrigger": [], + "databaseEventTrigger": [], + "fieldMetadata": [], + "index": [], + "objectMetadata": [], + "pageLayout": [], + "pageLayoutTab": [], + "pageLayoutWidget": [], + "role": [], + "roleTarget": [], + "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], + "serverlessFunction": [], + "view": [], + "viewField": [], + "viewFilter": [], + "viewFilterGroup": [ + { + "errors": [ + { + "code": "VIEW_NOT_FOUND", + "message": "View not found", + "userFriendlyMessage": "View not found", + }, + ], + "flatEntityMinimalInformation": { + "id": Any, + }, + "status": "fail", + "type": "create_view_filter_group", + }, + ], + "viewGroup": [], + }, + "message": "Validation failed for 0 object(s) and 0 field(s)", + "summary": { + "invalidAgent": 0, + "invalidCronTrigger": 0, + "invalidDatabaseEventTrigger": 0, + "invalidFieldMetadata": 0, + "invalidIndex": 0, + "invalidObjectMetadata": 0, + "invalidPageLayout": 0, + "invalidPageLayoutTab": 0, + "invalidPageLayoutWidget": 0, + "invalidRole": 0, + "invalidRoleTarget": 0, + "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, + "invalidServerlessFunction": 0, + "invalidView": 0, + "invalidViewField": 0, + "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, + "invalidViewGroup": 0, + "totalErrors": 0, + }, + "userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)", + }, + "message": "Multiple validation errors occurred while creating view filter group", + "name": "GraphQLError", +} +`; + +exports[`View Filter Group creation should fail when viewId is not a valid UUID 1`] = ` +{ + "extensions": { + "code": "GRAPHQL_VALIDATION_FAILED", + "http": { + "status": 400, + }, + "userFriendlyMessage": "An error occurred.", + "value": "invalid-uuid", + }, + "message": "Invalid UUID", + "name": "ValidationError", +} +`; diff --git a/packages/twenty-server/test/integration/metadata/suites/view-filter-group/__snapshots__/failing-view-filter-group-deletion.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/__snapshots__/failing-view-filter-group-deletion.integration-spec.ts.snap new file mode 100644 index 0000000000..7643e5d0a1 --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/__snapshots__/failing-view-filter-group-deletion.integration-spec.ts.snap @@ -0,0 +1,12 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`View Filter Group deletion should fail when view filter group id does not exist 1`] = ` +{ + "extensions": { + "code": "NOT_FOUND", + "userFriendlyMessage": "An error occurred.", + }, + "message": "View filter group not found", + "name": "NotFoundError", +} +`; diff --git a/packages/twenty-server/test/integration/metadata/suites/view-filter-group/__snapshots__/failing-view-filter-group-destroy.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/__snapshots__/failing-view-filter-group-destroy.integration-spec.ts.snap new file mode 100644 index 0000000000..1e7f4792f6 --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/__snapshots__/failing-view-filter-group-destroy.integration-spec.ts.snap @@ -0,0 +1,12 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`View Filter Group destroy should fail when view filter group id does not exist 1`] = ` +{ + "extensions": { + "code": "NOT_FOUND", + "userFriendlyMessage": "An error occurred.", + }, + "message": "View filter group not found", + "name": "NotFoundError", +} +`; diff --git a/packages/twenty-server/test/integration/metadata/suites/view-filter-group/__snapshots__/failing-view-filter-group-update-circular-dependency.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/__snapshots__/failing-view-filter-group-update-circular-dependency.integration-spec.ts.snap new file mode 100644 index 0000000000..8041f40789 --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/__snapshots__/failing-view-filter-group-update-circular-dependency.integration-spec.ts.snap @@ -0,0 +1,145 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`View Filter Group update should fail with circular dependency when parentViewFilterGroupId equals id (self-reference) 1`] = ` +{ + "extensions": { + "code": "METADATA_VALIDATION_FAILED", + "errors": { + "agent": [], + "cronTrigger": [], + "databaseEventTrigger": [], + "fieldMetadata": [], + "index": [], + "objectMetadata": [], + "pageLayout": [], + "pageLayoutTab": [], + "pageLayoutWidget": [], + "role": [], + "roleTarget": [], + "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], + "serverlessFunction": [], + "view": [], + "viewField": [], + "viewFilter": [], + "viewFilterGroup": [ + { + "errors": [ + { + "code": "CIRCULAR_DEPENDENCY", + "message": "View filter group cannot be its own parent", + "userFriendlyMessage": "View filter group cannot be its own parent", + }, + ], + "flatEntityMinimalInformation": { + "id": Any, + }, + "status": "fail", + "type": "update_view_filter_group", + }, + ], + "viewGroup": [], + }, + "message": "Validation failed for 0 object(s) and 0 field(s)", + "summary": { + "invalidAgent": 0, + "invalidCronTrigger": 0, + "invalidDatabaseEventTrigger": 0, + "invalidFieldMetadata": 0, + "invalidIndex": 0, + "invalidObjectMetadata": 0, + "invalidPageLayout": 0, + "invalidPageLayoutTab": 0, + "invalidPageLayoutWidget": 0, + "invalidRole": 0, + "invalidRoleTarget": 0, + "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, + "invalidServerlessFunction": 0, + "invalidView": 0, + "invalidViewField": 0, + "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, + "invalidViewGroup": 0, + "totalErrors": 0, + }, + "userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)", + }, + "message": "Multiple validation errors occurred while updating view filter group", + "name": "GraphQLError", +} +`; + +exports[`View Filter Group update should fail with circular dependency when update creates a circular dependency chain 1`] = ` +{ + "extensions": { + "code": "METADATA_VALIDATION_FAILED", + "errors": { + "agent": [], + "cronTrigger": [], + "databaseEventTrigger": [], + "fieldMetadata": [], + "index": [], + "objectMetadata": [], + "pageLayout": [], + "pageLayoutTab": [], + "pageLayoutWidget": [], + "role": [], + "roleTarget": [], + "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], + "serverlessFunction": [], + "view": [], + "viewField": [], + "viewFilter": [], + "viewFilterGroup": [ + { + "errors": [ + { + "code": "CIRCULAR_DEPENDENCY", + "message": "Circular dependency detected in view filter group hierarchy", + "userFriendlyMessage": "Circular dependency detected in view filter group hierarchy", + }, + ], + "flatEntityMinimalInformation": { + "id": Any, + }, + "status": "fail", + "type": "update_view_filter_group", + }, + ], + "viewGroup": [], + }, + "message": "Validation failed for 0 object(s) and 0 field(s)", + "summary": { + "invalidAgent": 0, + "invalidCronTrigger": 0, + "invalidDatabaseEventTrigger": 0, + "invalidFieldMetadata": 0, + "invalidIndex": 0, + "invalidObjectMetadata": 0, + "invalidPageLayout": 0, + "invalidPageLayoutTab": 0, + "invalidPageLayoutWidget": 0, + "invalidRole": 0, + "invalidRoleTarget": 0, + "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, + "invalidServerlessFunction": 0, + "invalidView": 0, + "invalidViewField": 0, + "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, + "invalidViewGroup": 0, + "totalErrors": 0, + }, + "userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)", + }, + "message": "Multiple validation errors occurred while updating view filter group", + "name": "GraphQLError", +} +`; diff --git a/packages/twenty-server/test/integration/metadata/suites/view-filter-group/__snapshots__/failing-view-filter-group-update.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/__snapshots__/failing-view-filter-group-update.integration-spec.ts.snap new file mode 100644 index 0000000000..220051da88 --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/__snapshots__/failing-view-filter-group-update.integration-spec.ts.snap @@ -0,0 +1,84 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`View Filter Group update should fail when parentViewFilterGroupId does not exist 1`] = ` +{ + "extensions": { + "code": "METADATA_VALIDATION_FAILED", + "errors": { + "agent": [], + "cronTrigger": [], + "databaseEventTrigger": [], + "fieldMetadata": [], + "index": [], + "objectMetadata": [], + "pageLayout": [], + "pageLayoutTab": [], + "pageLayoutWidget": [], + "role": [], + "roleTarget": [], + "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], + "serverlessFunction": [], + "view": [], + "viewField": [], + "viewFilter": [], + "viewFilterGroup": [ + { + "errors": [ + { + "code": "VIEW_FILTER_GROUP_NOT_FOUND", + "message": "Parent view filter group not found", + "userFriendlyMessage": "Parent view filter group not found", + }, + ], + "flatEntityMinimalInformation": { + "id": Any, + }, + "status": "fail", + "type": "update_view_filter_group", + }, + ], + "viewGroup": [], + }, + "message": "Validation failed for 0 object(s) and 0 field(s)", + "summary": { + "invalidAgent": 0, + "invalidCronTrigger": 0, + "invalidDatabaseEventTrigger": 0, + "invalidFieldMetadata": 0, + "invalidIndex": 0, + "invalidObjectMetadata": 0, + "invalidPageLayout": 0, + "invalidPageLayoutTab": 0, + "invalidPageLayoutWidget": 0, + "invalidRole": 0, + "invalidRoleTarget": 0, + "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, + "invalidServerlessFunction": 0, + "invalidView": 0, + "invalidViewField": 0, + "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, + "invalidViewGroup": 0, + "totalErrors": 0, + }, + "userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)", + }, + "message": "Multiple validation errors occurred while updating view filter group", + "name": "GraphQLError", +} +`; + +exports[`View Filter Group update should fail when view filter group id does not exist 1`] = ` +{ + "extensions": { + "code": "NOT_FOUND", + "userFriendlyMessage": "An error occurred.", + }, + "message": "View filter group not found", + "name": "NotFoundError", +} +`; diff --git a/packages/twenty-server/test/integration/metadata/suites/view-filter-group/failing-view-filter-group-creation-circular-dependency.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/failing-view-filter-group-creation-circular-dependency.integration-spec.ts new file mode 100644 index 0000000000..9c3f72297e --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/failing-view-filter-group-creation-circular-dependency.integration-spec.ts @@ -0,0 +1,136 @@ +import { expectOneNotInternalServerErrorSnapshot } from 'test/integration/graphql/utils/expect-one-not-internal-server-error-snapshot.util'; +import { findManyObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/find-many-object-metadata.util'; +import { createOneCoreViewFilterGroup } from 'test/integration/metadata/suites/view-filter-group/utils/create-one-core-view-filter-group.util'; +import { destroyOneCoreViewFilterGroup } from 'test/integration/metadata/suites/view-filter-group/utils/destroy-one-core-view-filter-group.util'; +import { createOneCoreView } from 'test/integration/metadata/suites/view/utils/create-one-core-view.util'; +import { destroyOneCoreView } from 'test/integration/metadata/suites/view/utils/destroy-one-core-view.util'; +import { jestExpectToBeDefined } from 'test/utils/jest-expect-to-be-defined.util.test'; +import { v4 } from 'uuid'; + +import { ViewFilterGroupLogicalOperator } from 'src/engine/metadata-modules/view-filter-group/enums/view-filter-group-logical-operator'; +import { ViewType } from 'src/engine/metadata-modules/view/enums/view-type.enum'; + +describe('View Filter Group creation should fail with circular dependency', () => { + let createdViewId: string; + let parentViewFilterGroupId: string; + let childViewFilterGroupId: string; + + beforeAll(async () => { + const { objects } = await findManyObjectMetadata({ + expectToFail: false, + input: { + filter: {}, + paging: { first: 1000 }, + }, + gqlFields: ` + id + nameSingular + `, + }); + + jestExpectToBeDefined(objects); + + const companyObjectMetadata = objects.find( + (object: { nameSingular: string }) => object.nameSingular === 'company', + ); + + jestExpectToBeDefined(companyObjectMetadata); + + const { data: viewData } = await createOneCoreView({ + expectToFail: false, + input: { + name: 'Test View For Creation Circular Dependency', + objectMetadataId: companyObjectMetadata.id, + type: ViewType.TABLE, + icon: 'IconBuildingSkyscraper', + }, + }); + + createdViewId = viewData?.createCoreView?.id; + jestExpectToBeDefined(createdViewId); + + // Create parent view filter group for max depth test + const { data: parentFilterGroupData } = await createOneCoreViewFilterGroup({ + expectToFail: false, + input: { + viewId: createdViewId, + logicalOperator: ViewFilterGroupLogicalOperator.AND, + }, + }); + + parentViewFilterGroupId = + parentFilterGroupData?.createCoreViewFilterGroup?.id; + jestExpectToBeDefined(parentViewFilterGroupId); + + // Create child view filter group for max depth test + const { data: childFilterGroupData } = await createOneCoreViewFilterGroup({ + expectToFail: false, + input: { + viewId: createdViewId, + logicalOperator: ViewFilterGroupLogicalOperator.AND, + parentViewFilterGroupId: parentViewFilterGroupId, + }, + }); + + childViewFilterGroupId = + childFilterGroupData?.createCoreViewFilterGroup?.id; + jestExpectToBeDefined(childViewFilterGroupId); + }); + + afterAll(async () => { + if (childViewFilterGroupId) { + await destroyOneCoreViewFilterGroup({ + expectToFail: false, + id: childViewFilterGroupId, + }); + } + + if (parentViewFilterGroupId) { + await destroyOneCoreViewFilterGroup({ + expectToFail: false, + id: parentViewFilterGroupId, + }); + } + + if (createdViewId) { + await destroyOneCoreView({ + expectToFail: false, + viewId: createdViewId, + }); + } + }); + + it('when id equals parentViewFilterGroupId (self-reference)', async () => { + const selfReferenceId = v4(); + + const { errors } = await createOneCoreViewFilterGroup({ + expectToFail: true, + input: { + id: selfReferenceId, + viewId: createdViewId, + logicalOperator: ViewFilterGroupLogicalOperator.AND, + parentViewFilterGroupId: selfReferenceId, + }, + }); + + expectOneNotInternalServerErrorSnapshot({ + errors, + }); + }); + + it('when max depth is exceeded during creation', async () => { + // Trying to create a grandchild (depth 3) when max depth is 2 + const { errors } = await createOneCoreViewFilterGroup({ + expectToFail: true, + input: { + viewId: createdViewId, + logicalOperator: ViewFilterGroupLogicalOperator.AND, + parentViewFilterGroupId: childViewFilterGroupId, + }, + }); + + expectOneNotInternalServerErrorSnapshot({ + errors, + }); + }); +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/view-filter-group/failing-view-filter-group-creation.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/failing-view-filter-group-creation.integration-spec.ts new file mode 100644 index 0000000000..460285b2b3 --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/failing-view-filter-group-creation.integration-spec.ts @@ -0,0 +1,125 @@ +import { expectOneNotInternalServerErrorSnapshot } from 'test/integration/graphql/utils/expect-one-not-internal-server-error-snapshot.util'; +import { findManyObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/find-many-object-metadata.util'; +import { createOneCoreViewFilterGroup } from 'test/integration/metadata/suites/view-filter-group/utils/create-one-core-view-filter-group.util'; +import { createOneCoreView } from 'test/integration/metadata/suites/view/utils/create-one-core-view.util'; +import { destroyOneCoreView } from 'test/integration/metadata/suites/view/utils/destroy-one-core-view.util'; +import { jestExpectToBeDefined } from 'test/utils/jest-expect-to-be-defined.util.test'; +import { + eachTestingContextFilter, + type EachTestingContext, +} from 'twenty-shared/testing'; +import { v4 } from 'uuid'; + +import { type CreateViewFilterGroupInput } from 'src/engine/metadata-modules/view-filter-group/dtos/inputs/create-view-filter-group.input'; +import { ViewType } from 'src/engine/metadata-modules/view/enums/view-type.enum'; + +type TestSetup = { + createdViewId: string; +}; + +type TestContext = { + input: (testSetup: TestSetup) => CreateViewFilterGroupInput; +}; + +const selfReferenceId = v4(); + +const failingViewFilterGroupCreationTestCases: EachTestingContext[] = + [ + { + title: 'when viewId is not a valid UUID', + context: { + input: () => ({ + viewId: 'invalid-uuid', + }), + }, + }, + { + title: 'when viewId does not exist', + context: { + input: () => ({ + viewId: v4(), + }), + }, + }, + { + title: 'when parentViewFilterGroupId does not exist', + context: { + input: (testSetup) => ({ + viewId: testSetup.createdViewId, + parentViewFilterGroupId: v4(), + }), + }, + }, + { + title: 'when parentViewFilterGroupId equals id (self-reference)', + context: { + input: (testSetup) => ({ + id: selfReferenceId, + viewId: testSetup.createdViewId, + parentViewFilterGroupId: selfReferenceId, + }), + }, + }, + ]; + +describe('View Filter Group creation should fail', () => { + let createdViewId: string; + + beforeAll(async () => { + const { objects } = await findManyObjectMetadata({ + expectToFail: false, + input: { + filter: {}, + paging: { first: 1000 }, + }, + gqlFields: ` + id + nameSingular + `, + }); + + jestExpectToBeDefined(objects); + + const companyObjectMetadata = objects.find( + (object: { nameSingular: string }) => object.nameSingular === 'company', + ); + + jestExpectToBeDefined(companyObjectMetadata); + + const { data: viewData } = await createOneCoreView({ + expectToFail: false, + input: { + name: 'Test View For Failing Filter Group Creation', + objectMetadataId: companyObjectMetadata.id, + type: ViewType.TABLE, + icon: 'IconBuildingSkyscraper', + }, + }); + + createdViewId = viewData?.createCoreView?.id; + jestExpectToBeDefined(createdViewId); + }); + + afterAll(async () => { + if (createdViewId) { + await destroyOneCoreView({ + expectToFail: false, + viewId: createdViewId, + }); + } + }); + + it.each(eachTestingContextFilter(failingViewFilterGroupCreationTestCases))( + '$title', + async ({ context }) => { + const { errors } = await createOneCoreViewFilterGroup({ + expectToFail: true, + input: context.input({ createdViewId }), + }); + + expectOneNotInternalServerErrorSnapshot({ + errors, + }); + }, + ); +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/view-filter-group/failing-view-filter-group-deletion.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/failing-view-filter-group-deletion.integration-spec.ts new file mode 100644 index 0000000000..505e7491fb --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/failing-view-filter-group-deletion.integration-spec.ts @@ -0,0 +1,37 @@ +import { expectOneNotInternalServerErrorSnapshot } from 'test/integration/graphql/utils/expect-one-not-internal-server-error-snapshot.util'; +import { deleteOneCoreViewFilterGroup } from 'test/integration/metadata/suites/view-filter-group/utils/delete-one-core-view-filter-group.util'; +import { v4 } from 'uuid'; +import { + eachTestingContextFilter, + type EachTestingContext, +} from 'twenty-shared/testing'; + +type TestContext = { + id: string; +}; + +const failingViewFilterGroupDeletionTestCases: EachTestingContext[] = + [ + { + title: 'when view filter group id does not exist', + context: { + id: v4(), + }, + }, + ]; + +describe('View Filter Group deletion should fail', () => { + it.each(eachTestingContextFilter(failingViewFilterGroupDeletionTestCases))( + '$title', + async ({ context }) => { + const { errors } = await deleteOneCoreViewFilterGroup({ + id: context.id, + expectToFail: true, + }); + + expectOneNotInternalServerErrorSnapshot({ + errors, + }); + }, + ); +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/view-filter-group/failing-view-filter-group-destroy.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/failing-view-filter-group-destroy.integration-spec.ts new file mode 100644 index 0000000000..6c93cefb19 --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/failing-view-filter-group-destroy.integration-spec.ts @@ -0,0 +1,37 @@ +import { expectOneNotInternalServerErrorSnapshot } from 'test/integration/graphql/utils/expect-one-not-internal-server-error-snapshot.util'; +import { destroyOneCoreViewFilterGroup } from 'test/integration/metadata/suites/view-filter-group/utils/destroy-one-core-view-filter-group.util'; +import { v4 } from 'uuid'; +import { + eachTestingContextFilter, + type EachTestingContext, +} from 'twenty-shared/testing'; + +type TestContext = { + id: string; +}; + +const failingViewFilterGroupDestroyTestCases: EachTestingContext[] = + [ + { + title: 'when view filter group id does not exist', + context: { + id: v4(), + }, + }, + ]; + +describe('View Filter Group destroy should fail', () => { + it.each(eachTestingContextFilter(failingViewFilterGroupDestroyTestCases))( + '$title', + async ({ context }) => { + const { errors } = await destroyOneCoreViewFilterGroup({ + id: context.id, + expectToFail: true, + }); + + expectOneNotInternalServerErrorSnapshot({ + errors, + }); + }, + ); +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/view-filter-group/failing-view-filter-group-update-circular-dependency.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/failing-view-filter-group-update-circular-dependency.integration-spec.ts new file mode 100644 index 0000000000..92c49e65c1 --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/failing-view-filter-group-update-circular-dependency.integration-spec.ts @@ -0,0 +1,152 @@ +import { expectOneNotInternalServerErrorSnapshot } from 'test/integration/graphql/utils/expect-one-not-internal-server-error-snapshot.util'; +import { findManyObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/find-many-object-metadata.util'; +import { createOneCoreViewFilterGroup } from 'test/integration/metadata/suites/view-filter-group/utils/create-one-core-view-filter-group.util'; +import { destroyOneCoreViewFilterGroup } from 'test/integration/metadata/suites/view-filter-group/utils/destroy-one-core-view-filter-group.util'; +import { updateOneCoreViewFilterGroup } from 'test/integration/metadata/suites/view-filter-group/utils/update-one-core-view-filter-group.util'; +import { createOneCoreView } from 'test/integration/metadata/suites/view/utils/create-one-core-view.util'; +import { destroyOneCoreView } from 'test/integration/metadata/suites/view/utils/destroy-one-core-view.util'; +import { jestExpectToBeDefined } from 'test/utils/jest-expect-to-be-defined.util.test'; + +import { ViewFilterGroupLogicalOperator } from 'src/engine/metadata-modules/view-filter-group/enums/view-filter-group-logical-operator'; +import { ViewType } from 'src/engine/metadata-modules/view/enums/view-type.enum'; + +describe('View Filter Group update should fail with circular dependency', () => { + let createdViewId: string; + let viewFilterGroupId: string; + let parentViewFilterGroupId: string; + let childViewFilterGroupId: string; + + beforeAll(async () => { + const { objects } = await findManyObjectMetadata({ + expectToFail: false, + input: { + filter: {}, + paging: { first: 1000 }, + }, + gqlFields: ` + id + nameSingular + `, + }); + + jestExpectToBeDefined(objects); + + const companyObjectMetadata = objects.find( + (object: { nameSingular: string }) => object.nameSingular === 'company', + ); + + jestExpectToBeDefined(companyObjectMetadata); + + const { data: viewData } = await createOneCoreView({ + expectToFail: false, + input: { + name: 'Test View For Update Circular Dependency', + objectMetadataId: companyObjectMetadata.id, + type: ViewType.TABLE, + icon: 'IconBuildingSkyscraper', + }, + }); + + createdViewId = viewData?.createCoreView?.id; + jestExpectToBeDefined(createdViewId); + + // Create a standalone view filter group for self-reference test + const { data: filterGroupData } = await createOneCoreViewFilterGroup({ + expectToFail: false, + input: { + viewId: createdViewId, + logicalOperator: ViewFilterGroupLogicalOperator.AND, + }, + }); + + viewFilterGroupId = filterGroupData?.createCoreViewFilterGroup?.id; + jestExpectToBeDefined(viewFilterGroupId); + + // Create parent view filter group for chain test + const { data: parentFilterGroupData } = await createOneCoreViewFilterGroup({ + expectToFail: false, + input: { + viewId: createdViewId, + logicalOperator: ViewFilterGroupLogicalOperator.AND, + }, + }); + + parentViewFilterGroupId = + parentFilterGroupData?.createCoreViewFilterGroup?.id; + jestExpectToBeDefined(parentViewFilterGroupId); + + // Create child view filter group with parent reference for chain test + const { data: childFilterGroupData } = await createOneCoreViewFilterGroup({ + expectToFail: false, + input: { + viewId: createdViewId, + logicalOperator: ViewFilterGroupLogicalOperator.AND, + parentViewFilterGroupId: parentViewFilterGroupId, + }, + }); + + childViewFilterGroupId = + childFilterGroupData?.createCoreViewFilterGroup?.id; + jestExpectToBeDefined(childViewFilterGroupId); + }); + + afterAll(async () => { + // Delete child first (due to foreign key constraint) + if (childViewFilterGroupId) { + await destroyOneCoreViewFilterGroup({ + expectToFail: false, + id: childViewFilterGroupId, + }); + } + + if (parentViewFilterGroupId) { + await destroyOneCoreViewFilterGroup({ + expectToFail: false, + id: parentViewFilterGroupId, + }); + } + + if (viewFilterGroupId) { + await destroyOneCoreViewFilterGroup({ + expectToFail: false, + id: viewFilterGroupId, + }); + } + + if (createdViewId) { + await destroyOneCoreView({ + expectToFail: false, + viewId: createdViewId, + }); + } + }); + + it('when parentViewFilterGroupId equals id (self-reference)', async () => { + const { errors } = await updateOneCoreViewFilterGroup({ + id: viewFilterGroupId, + expectToFail: true, + input: { + parentViewFilterGroupId: viewFilterGroupId, + }, + }); + + expectOneNotInternalServerErrorSnapshot({ + errors, + }); + }); + + it('when update creates a circular dependency chain', async () => { + // Try to set parent's parent to be the child (parent → child, then child → parent = cycle) + const { errors } = await updateOneCoreViewFilterGroup({ + id: parentViewFilterGroupId, + expectToFail: true, + input: { + parentViewFilterGroupId: childViewFilterGroupId, + }, + }); + + expectOneNotInternalServerErrorSnapshot({ + errors, + }); + }); +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/view-filter-group/failing-view-filter-group-update.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/failing-view-filter-group-update.integration-spec.ts new file mode 100644 index 0000000000..2837614ce6 --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/failing-view-filter-group-update.integration-spec.ts @@ -0,0 +1,129 @@ +import { expectOneNotInternalServerErrorSnapshot } from 'test/integration/graphql/utils/expect-one-not-internal-server-error-snapshot.util'; +import { findManyObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/find-many-object-metadata.util'; +import { createOneCoreViewFilterGroup } from 'test/integration/metadata/suites/view-filter-group/utils/create-one-core-view-filter-group.util'; +import { destroyOneCoreViewFilterGroup } from 'test/integration/metadata/suites/view-filter-group/utils/destroy-one-core-view-filter-group.util'; +import { updateOneCoreViewFilterGroup } from 'test/integration/metadata/suites/view-filter-group/utils/update-one-core-view-filter-group.util'; +import { createOneCoreView } from 'test/integration/metadata/suites/view/utils/create-one-core-view.util'; +import { destroyOneCoreView } from 'test/integration/metadata/suites/view/utils/destroy-one-core-view.util'; +import { jestExpectToBeDefined } from 'test/utils/jest-expect-to-be-defined.util.test'; +import { + eachTestingContextFilter, + type EachTestingContext, +} from 'twenty-shared/testing'; +import { v4 } from 'uuid'; + +import { type UpdateViewFilterGroupInput } from 'src/engine/metadata-modules/view-filter-group/dtos/inputs/update-view-filter-group.input'; +import { ViewFilterGroupLogicalOperator } from 'src/engine/metadata-modules/view-filter-group/enums/view-filter-group-logical-operator'; +import { ViewType } from 'src/engine/metadata-modules/view/enums/view-type.enum'; + +type TestContext = { + id: string; + input: UpdateViewFilterGroupInput; +}; + +const failingViewFilterGroupUpdateTestCases: EachTestingContext[] = + [ + { + title: 'when view filter group id does not exist', + context: { + id: v4(), + input: { + logicalOperator: ViewFilterGroupLogicalOperator.OR, + }, + }, + }, + { + title: 'when parentViewFilterGroupId does not exist', + context: { + id: 'USE_CREATED_ID', + input: { + parentViewFilterGroupId: v4(), + }, + }, + }, + ]; + +describe('View Filter Group update should fail', () => { + let createdViewId: string; + let createdViewFilterGroupId: string; + + beforeAll(async () => { + const { objects } = await findManyObjectMetadata({ + expectToFail: false, + input: { + filter: {}, + paging: { first: 1000 }, + }, + gqlFields: ` + id + nameSingular + `, + }); + + jestExpectToBeDefined(objects); + + const companyObjectMetadata = objects.find( + (object: { nameSingular: string }) => object.nameSingular === 'company', + ); + + jestExpectToBeDefined(companyObjectMetadata); + + const { data: viewData } = await createOneCoreView({ + expectToFail: false, + input: { + name: 'Test View For Failing Filter Group Update', + objectMetadataId: companyObjectMetadata.id, + type: ViewType.TABLE, + icon: 'IconBuildingSkyscraper', + }, + }); + + createdViewId = viewData?.createCoreView?.id; + jestExpectToBeDefined(createdViewId); + + const { data: filterGroupData } = await createOneCoreViewFilterGroup({ + expectToFail: false, + input: { + viewId: createdViewId, + logicalOperator: ViewFilterGroupLogicalOperator.AND, + }, + }); + + createdViewFilterGroupId = filterGroupData?.createCoreViewFilterGroup?.id; + jestExpectToBeDefined(createdViewFilterGroupId); + }); + + afterAll(async () => { + if (createdViewFilterGroupId) { + await destroyOneCoreViewFilterGroup({ + expectToFail: false, + id: createdViewFilterGroupId, + }); + } + + if (createdViewId) { + await destroyOneCoreView({ + expectToFail: false, + viewId: createdViewId, + }); + } + }); + + it.each(eachTestingContextFilter(failingViewFilterGroupUpdateTestCases))( + '$title', + async ({ context }) => { + const id = + context.id === 'USE_CREATED_ID' ? createdViewFilterGroupId : context.id; + + const { errors } = await updateOneCoreViewFilterGroup({ + id, + expectToFail: true, + input: context.input, + }); + + expectOneNotInternalServerErrorSnapshot({ + errors, + }); + }, + ); +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/view-filter-group/successful-view-filter-group-creation.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/successful-view-filter-group-creation.integration-spec.ts new file mode 100644 index 0000000000..81e5492115 --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/successful-view-filter-group-creation.integration-spec.ts @@ -0,0 +1,148 @@ +import { findManyObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/find-many-object-metadata.util'; +import { createOneCoreViewFilterGroup } from 'test/integration/metadata/suites/view-filter-group/utils/create-one-core-view-filter-group.util'; +import { destroyOneCoreViewFilterGroup } from 'test/integration/metadata/suites/view-filter-group/utils/destroy-one-core-view-filter-group.util'; +import { createOneCoreView } from 'test/integration/metadata/suites/view/utils/create-one-core-view.util'; +import { destroyOneCoreView } from 'test/integration/metadata/suites/view/utils/destroy-one-core-view.util'; +import { jestExpectToBeDefined } from 'test/utils/jest-expect-to-be-defined.util.test'; + +import { ViewFilterGroupLogicalOperator } from 'src/engine/metadata-modules/view-filter-group/enums/view-filter-group-logical-operator'; +import { ViewType } from 'src/engine/metadata-modules/view/enums/view-type.enum'; + +describe('View Filter Group creation should succeed', () => { + let companyObjectMetadataId: string; + let createdViewId: string; + let createdViewFilterGroupId: string; + + beforeAll(async () => { + const { objects } = await findManyObjectMetadata({ + expectToFail: false, + input: { + filter: {}, + paging: { first: 1000 }, + }, + gqlFields: ` + id + nameSingular + `, + }); + + jestExpectToBeDefined(objects); + + const companyObjectMetadata = objects.find( + (object: { nameSingular: string }) => object.nameSingular === 'company', + ); + + jestExpectToBeDefined(companyObjectMetadata); + companyObjectMetadataId = companyObjectMetadata.id; + + const { data: viewData } = await createOneCoreView({ + expectToFail: false, + input: { + name: 'Test View For Filter Group', + objectMetadataId: companyObjectMetadataId, + type: ViewType.TABLE, + icon: 'IconBuildingSkyscraper', + }, + }); + + createdViewId = viewData?.createCoreView?.id; + }); + + afterAll(async () => { + if (createdViewId) { + await destroyOneCoreView({ + expectToFail: false, + viewId: createdViewId, + }); + } + }); + + afterEach(async () => { + if (createdViewFilterGroupId) { + await destroyOneCoreViewFilterGroup({ + expectToFail: false, + id: createdViewFilterGroupId, + }); + createdViewFilterGroupId = ''; + } + }); + + it('should create a view filter group with minimal input', async () => { + const { data } = await createOneCoreViewFilterGroup({ + expectToFail: false, + input: { + viewId: createdViewId, + }, + }); + + createdViewFilterGroupId = data?.createCoreViewFilterGroup?.id; + + expect(data.createCoreViewFilterGroup).toMatchObject({ + id: expect.any(String), + viewId: createdViewId, + logicalOperator: ViewFilterGroupLogicalOperator.AND, + parentViewFilterGroupId: null, + }); + }); + + it('should create a view filter group with all fields', async () => { + const { data } = await createOneCoreViewFilterGroup({ + expectToFail: false, + input: { + viewId: createdViewId, + logicalOperator: ViewFilterGroupLogicalOperator.OR, + positionInViewFilterGroup: 1, + }, + }); + + createdViewFilterGroupId = data?.createCoreViewFilterGroup?.id; + + expect(data.createCoreViewFilterGroup).toMatchObject({ + id: expect.any(String), + viewId: createdViewId, + logicalOperator: ViewFilterGroupLogicalOperator.OR, + parentViewFilterGroupId: null, + }); + }); + + it('should create a nested view filter group', async () => { + // Create parent filter group + const { data: parentData } = await createOneCoreViewFilterGroup({ + expectToFail: false, + input: { + viewId: createdViewId, + logicalOperator: ViewFilterGroupLogicalOperator.AND, + }, + }); + + const parentViewFilterGroupId = parentData?.createCoreViewFilterGroup?.id; + + // Create child filter group + const { data: childData } = await createOneCoreViewFilterGroup({ + expectToFail: false, + input: { + viewId: createdViewId, + logicalOperator: ViewFilterGroupLogicalOperator.OR, + parentViewFilterGroupId, + }, + }); + + createdViewFilterGroupId = childData?.createCoreViewFilterGroup?.id; + + expect(childData.createCoreViewFilterGroup).toMatchObject({ + id: expect.any(String), + viewId: createdViewId, + logicalOperator: ViewFilterGroupLogicalOperator.OR, + parentViewFilterGroupId, + }); + + // Clean up parent (this CASCADE-deletes the child too) + await destroyOneCoreViewFilterGroup({ + expectToFail: false, + id: parentViewFilterGroupId, + }); + + // Clear the child ID since it was CASCADE-deleted with the parent + createdViewFilterGroupId = ''; + }); +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/view-filter-group/successful-view-filter-group-deletion.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/successful-view-filter-group-deletion.integration-spec.ts new file mode 100644 index 0000000000..fae89f4cb3 --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/successful-view-filter-group-deletion.integration-spec.ts @@ -0,0 +1,82 @@ +import { findManyObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/find-many-object-metadata.util'; +import { createOneCoreViewFilterGroup } from 'test/integration/metadata/suites/view-filter-group/utils/create-one-core-view-filter-group.util'; +import { deleteOneCoreViewFilterGroup } from 'test/integration/metadata/suites/view-filter-group/utils/delete-one-core-view-filter-group.util'; +import { destroyOneCoreViewFilterGroup } from 'test/integration/metadata/suites/view-filter-group/utils/destroy-one-core-view-filter-group.util'; +import { createOneCoreView } from 'test/integration/metadata/suites/view/utils/create-one-core-view.util'; +import { destroyOneCoreView } from 'test/integration/metadata/suites/view/utils/destroy-one-core-view.util'; +import { jestExpectToBeDefined } from 'test/utils/jest-expect-to-be-defined.util.test'; + +import { ViewFilterGroupLogicalOperator } from 'src/engine/metadata-modules/view-filter-group/enums/view-filter-group-logical-operator'; +import { ViewType } from 'src/engine/metadata-modules/view/enums/view-type.enum'; + +describe('View Filter Group deletion should succeed', () => { + let createdViewId: string; + + beforeAll(async () => { + const { objects } = await findManyObjectMetadata({ + expectToFail: false, + input: { + filter: {}, + paging: { first: 1000 }, + }, + gqlFields: ` + id + nameSingular + `, + }); + + jestExpectToBeDefined(objects); + + const companyObjectMetadata = objects.find( + (object: { nameSingular: string }) => object.nameSingular === 'company', + ); + + jestExpectToBeDefined(companyObjectMetadata); + + const { data: viewData } = await createOneCoreView({ + expectToFail: false, + input: { + name: 'Test View For Filter Group Deletion', + objectMetadataId: companyObjectMetadata.id, + type: ViewType.TABLE, + icon: 'IconBuildingSkyscraper', + }, + }); + + createdViewId = viewData?.createCoreView?.id; + }); + + afterAll(async () => { + if (createdViewId) { + await destroyOneCoreView({ + expectToFail: false, + viewId: createdViewId, + }); + } + }); + + it('should soft delete a view filter group', async () => { + const { data: createData } = await createOneCoreViewFilterGroup({ + expectToFail: false, + input: { + viewId: createdViewId, + logicalOperator: ViewFilterGroupLogicalOperator.AND, + }, + }); + + const createdViewFilterGroupId = createData?.createCoreViewFilterGroup?.id; + + const { data: deleteData } = await deleteOneCoreViewFilterGroup({ + id: createdViewFilterGroupId, + expectToFail: false, + }); + + expect(deleteData.deleteCoreViewFilterGroup).toBe(true); + + // Clean up by destroying + await destroyOneCoreViewFilterGroup({ + expectToFail: false, + id: createdViewFilterGroupId, + }); + }); +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/view-filter-group/successful-view-filter-group-destroy.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/successful-view-filter-group-destroy.integration-spec.ts new file mode 100644 index 0000000000..b3d5c44188 --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/successful-view-filter-group-destroy.integration-spec.ts @@ -0,0 +1,75 @@ +import { findManyObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/find-many-object-metadata.util'; +import { createOneCoreViewFilterGroup } from 'test/integration/metadata/suites/view-filter-group/utils/create-one-core-view-filter-group.util'; +import { destroyOneCoreViewFilterGroup } from 'test/integration/metadata/suites/view-filter-group/utils/destroy-one-core-view-filter-group.util'; +import { createOneCoreView } from 'test/integration/metadata/suites/view/utils/create-one-core-view.util'; +import { destroyOneCoreView } from 'test/integration/metadata/suites/view/utils/destroy-one-core-view.util'; +import { jestExpectToBeDefined } from 'test/utils/jest-expect-to-be-defined.util.test'; + +import { ViewFilterGroupLogicalOperator } from 'src/engine/metadata-modules/view-filter-group/enums/view-filter-group-logical-operator'; +import { ViewType } from 'src/engine/metadata-modules/view/enums/view-type.enum'; + +describe('View Filter Group destroy should succeed', () => { + let createdViewId: string; + + beforeAll(async () => { + const { objects } = await findManyObjectMetadata({ + expectToFail: false, + input: { + filter: {}, + paging: { first: 1000 }, + }, + gqlFields: ` + id + nameSingular + `, + }); + + jestExpectToBeDefined(objects); + + const companyObjectMetadata = objects.find( + (object: { nameSingular: string }) => object.nameSingular === 'company', + ); + + jestExpectToBeDefined(companyObjectMetadata); + + const { data: viewData } = await createOneCoreView({ + expectToFail: false, + input: { + name: 'Test View For Filter Group Destroy', + objectMetadataId: companyObjectMetadata.id, + type: ViewType.TABLE, + icon: 'IconBuildingSkyscraper', + }, + }); + + createdViewId = viewData?.createCoreView?.id; + }); + + afterAll(async () => { + if (createdViewId) { + await destroyOneCoreView({ + expectToFail: false, + viewId: createdViewId, + }); + } + }); + + it('should destroy a view filter group', async () => { + const { data: createData } = await createOneCoreViewFilterGroup({ + expectToFail: false, + input: { + viewId: createdViewId, + logicalOperator: ViewFilterGroupLogicalOperator.OR, + }, + }); + + const createdViewFilterGroupId = createData?.createCoreViewFilterGroup?.id; + + const { data: destroyData } = await destroyOneCoreViewFilterGroup({ + id: createdViewFilterGroupId, + expectToFail: false, + }); + + expect(destroyData.destroyCoreViewFilterGroup).toBe(true); + }); +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/view-filter-group/successful-view-filter-group-update.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/successful-view-filter-group-update.integration-spec.ts new file mode 100644 index 0000000000..0aecc39fd6 --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/successful-view-filter-group-update.integration-spec.ts @@ -0,0 +1,112 @@ +import { findManyObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/find-many-object-metadata.util'; +import { createOneCoreViewFilterGroup } from 'test/integration/metadata/suites/view-filter-group/utils/create-one-core-view-filter-group.util'; +import { destroyOneCoreViewFilterGroup } from 'test/integration/metadata/suites/view-filter-group/utils/destroy-one-core-view-filter-group.util'; +import { updateOneCoreViewFilterGroup } from 'test/integration/metadata/suites/view-filter-group/utils/update-one-core-view-filter-group.util'; +import { createOneCoreView } from 'test/integration/metadata/suites/view/utils/create-one-core-view.util'; +import { destroyOneCoreView } from 'test/integration/metadata/suites/view/utils/destroy-one-core-view.util'; +import { jestExpectToBeDefined } from 'test/utils/jest-expect-to-be-defined.util.test'; + +import { ViewFilterGroupLogicalOperator } from 'src/engine/metadata-modules/view-filter-group/enums/view-filter-group-logical-operator'; +import { ViewType } from 'src/engine/metadata-modules/view/enums/view-type.enum'; + +describe('View Filter Group update should succeed', () => { + let companyObjectMetadataId: string; + let createdViewId: string; + let createdViewFilterGroupId: string; + + beforeAll(async () => { + const { objects } = await findManyObjectMetadata({ + expectToFail: false, + input: { + filter: {}, + paging: { first: 1000 }, + }, + gqlFields: ` + id + nameSingular + `, + }); + + jestExpectToBeDefined(objects); + + const companyObjectMetadata = objects.find( + (object: { nameSingular: string }) => object.nameSingular === 'company', + ); + + jestExpectToBeDefined(companyObjectMetadata); + companyObjectMetadataId = companyObjectMetadata.id; + + const { data: viewData } = await createOneCoreView({ + expectToFail: false, + input: { + name: 'Test View For Filter Group Update', + objectMetadataId: companyObjectMetadataId, + type: ViewType.TABLE, + icon: 'IconBuildingSkyscraper', + }, + }); + + createdViewId = viewData?.createCoreView?.id; + }); + + afterAll(async () => { + if (createdViewId) { + await destroyOneCoreView({ + expectToFail: false, + viewId: createdViewId, + }); + } + }); + + beforeEach(async () => { + const { data } = await createOneCoreViewFilterGroup({ + expectToFail: false, + input: { + viewId: createdViewId, + logicalOperator: ViewFilterGroupLogicalOperator.AND, + }, + }); + + createdViewFilterGroupId = data?.createCoreViewFilterGroup?.id; + }); + + afterEach(async () => { + if (createdViewFilterGroupId) { + await destroyOneCoreViewFilterGroup({ + expectToFail: false, + id: createdViewFilterGroupId, + }); + createdViewFilterGroupId = ''; + } + }); + + it('should update the logical operator', async () => { + const { data } = await updateOneCoreViewFilterGroup({ + id: createdViewFilterGroupId, + expectToFail: false, + input: { + logicalOperator: ViewFilterGroupLogicalOperator.OR, + }, + }); + + expect(data.updateCoreViewFilterGroup).toMatchObject({ + id: createdViewFilterGroupId, + logicalOperator: ViewFilterGroupLogicalOperator.OR, + }); + }); + + it('should update the position', async () => { + const { data } = await updateOneCoreViewFilterGroup({ + id: createdViewFilterGroupId, + expectToFail: false, + input: { + positionInViewFilterGroup: 5, + }, + }); + + expect(data.updateCoreViewFilterGroup).toMatchObject({ + id: createdViewFilterGroupId, + positionInViewFilterGroup: 5, + }); + }); +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/view-filter-group/utils/create-core-view-filter-group-query-factory.util.ts b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/utils/create-core-view-filter-group-query-factory.util.ts new file mode 100644 index 0000000000..38446855a7 --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/utils/create-core-view-filter-group-query-factory.util.ts @@ -0,0 +1,21 @@ +import gql from 'graphql-tag'; +import { VIEW_FILTER_GROUP_GQL_FIELDS } from 'test/integration/constants/view-gql-fields.constants'; +import { type PerformMetadataQueryParams } from 'test/integration/metadata/types/perform-metadata-query.type'; + +import { type CreateViewFilterGroupInput } from 'src/engine/metadata-modules/view-filter-group/dtos/inputs/create-view-filter-group.input'; + +export const createCoreViewFilterGroupQueryFactory = ({ + gqlFields = VIEW_FILTER_GROUP_GQL_FIELDS, + input, +}: PerformMetadataQueryParams) => ({ + query: gql` + mutation CreateCoreViewFilterGroup($input: CreateViewFilterGroupInput!) { + createCoreViewFilterGroup(input: $input) { + ${gqlFields} + } + } + `, + variables: { + input, + }, +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/view-filter-group/utils/create-one-core-view-filter-group.util.ts b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/utils/create-one-core-view-filter-group.util.ts new file mode 100644 index 0000000000..ed996967ea --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/utils/create-one-core-view-filter-group.util.ts @@ -0,0 +1,40 @@ +import { makeMetadataAPIRequest } from 'test/integration/metadata/suites/utils/make-metadata-api-request.util'; +import { createCoreViewFilterGroupQueryFactory } from 'test/integration/metadata/suites/view-filter-group/utils/create-core-view-filter-group-query-factory.util'; +import { type CommonResponseBody } from 'test/integration/metadata/types/common-response-body.type'; +import { type PerformMetadataQueryParams } from 'test/integration/metadata/types/perform-metadata-query.type'; +import { warnIfErrorButNotExpectedToFail } from 'test/integration/metadata/utils/warn-if-error-but-not-expected-to-fail.util'; +import { warnIfNoErrorButExpectedToFail } from 'test/integration/metadata/utils/warn-if-no-error-but-expected-to-fail.util'; + +import { type CreateViewFilterGroupInput } from 'src/engine/metadata-modules/view-filter-group/dtos/inputs/create-view-filter-group.input'; +import { type ViewFilterGroupDTO } from 'src/engine/metadata-modules/view-filter-group/dtos/view-filter-group.dto'; + +export const createOneCoreViewFilterGroup = async ({ + input, + gqlFields, + expectToFail, +}: PerformMetadataQueryParams): CommonResponseBody<{ + createCoreViewFilterGroup: ViewFilterGroupDTO; +}> => { + const graphqlOperation = createCoreViewFilterGroupQueryFactory({ + input, + gqlFields, + }); + + const response = await makeMetadataAPIRequest(graphqlOperation); + + if (expectToFail === true) { + warnIfNoErrorButExpectedToFail({ + response, + errorMessage: 'View Filter Group creation should have failed but did not', + }); + } + + if (expectToFail === false) { + warnIfErrorButNotExpectedToFail({ + response, + errorMessage: 'View Filter Group creation has failed but should not', + }); + } + + return { data: response.body.data, errors: response.body.errors }; +}; diff --git a/packages/twenty-server/test/integration/metadata/suites/view-filter-group/utils/delete-core-view-filter-group-query-factory.util.ts b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/utils/delete-core-view-filter-group-query-factory.util.ts new file mode 100644 index 0000000000..bf0bcf7e09 --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/utils/delete-core-view-filter-group-query-factory.util.ts @@ -0,0 +1,16 @@ +import gql from 'graphql-tag'; + +export const deleteCoreViewFilterGroupQueryFactory = ({ + id, +}: { + id: string; +}) => ({ + query: gql` + mutation DeleteCoreViewFilterGroup($id: String!) { + deleteCoreViewFilterGroup(id: $id) + } + `, + variables: { + id, + }, +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/view-filter-group/utils/delete-one-core-view-filter-group.util.ts b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/utils/delete-one-core-view-filter-group.util.ts new file mode 100644 index 0000000000..27beaae45b --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/utils/delete-one-core-view-filter-group.util.ts @@ -0,0 +1,35 @@ +import { makeMetadataAPIRequest } from 'test/integration/metadata/suites/utils/make-metadata-api-request.util'; +import { deleteCoreViewFilterGroupQueryFactory } from 'test/integration/metadata/suites/view-filter-group/utils/delete-core-view-filter-group-query-factory.util'; +import { type CommonResponseBody } from 'test/integration/metadata/types/common-response-body.type'; +import { warnIfErrorButNotExpectedToFail } from 'test/integration/metadata/utils/warn-if-error-but-not-expected-to-fail.util'; +import { warnIfNoErrorButExpectedToFail } from 'test/integration/metadata/utils/warn-if-no-error-but-expected-to-fail.util'; + +export const deleteOneCoreViewFilterGroup = async ({ + id, + expectToFail, +}: { + id: string; + expectToFail: boolean; +}): CommonResponseBody<{ + deleteCoreViewFilterGroup: boolean; +}> => { + const graphqlOperation = deleteCoreViewFilterGroupQueryFactory({ id }); + + const response = await makeMetadataAPIRequest(graphqlOperation); + + if (expectToFail === true) { + warnIfNoErrorButExpectedToFail({ + response, + errorMessage: 'View Filter Group deletion should have failed but did not', + }); + } + + if (expectToFail === false) { + warnIfErrorButNotExpectedToFail({ + response, + errorMessage: 'View Filter Group deletion has failed but should not', + }); + } + + return { data: response.body.data, errors: response.body.errors }; +}; diff --git a/packages/twenty-server/test/integration/metadata/suites/view-filter-group/utils/destroy-core-view-filter-group-query-factory.util.ts b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/utils/destroy-core-view-filter-group-query-factory.util.ts new file mode 100644 index 0000000000..23817fdfd8 --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/utils/destroy-core-view-filter-group-query-factory.util.ts @@ -0,0 +1,16 @@ +import gql from 'graphql-tag'; + +export const destroyCoreViewFilterGroupQueryFactory = ({ + id, +}: { + id: string; +}) => ({ + query: gql` + mutation DestroyCoreViewFilterGroup($id: String!) { + destroyCoreViewFilterGroup(id: $id) + } + `, + variables: { + id, + }, +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/view-filter-group/utils/destroy-one-core-view-filter-group.util.ts b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/utils/destroy-one-core-view-filter-group.util.ts new file mode 100644 index 0000000000..55fa379a72 --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/utils/destroy-one-core-view-filter-group.util.ts @@ -0,0 +1,36 @@ +import { makeMetadataAPIRequest } from 'test/integration/metadata/suites/utils/make-metadata-api-request.util'; +import { destroyCoreViewFilterGroupQueryFactory } from 'test/integration/metadata/suites/view-filter-group/utils/destroy-core-view-filter-group-query-factory.util'; +import { type CommonResponseBody } from 'test/integration/metadata/types/common-response-body.type'; +import { warnIfErrorButNotExpectedToFail } from 'test/integration/metadata/utils/warn-if-error-but-not-expected-to-fail.util'; +import { warnIfNoErrorButExpectedToFail } from 'test/integration/metadata/utils/warn-if-no-error-but-expected-to-fail.util'; + +export const destroyOneCoreViewFilterGroup = async ({ + id, + expectToFail, +}: { + id: string; + expectToFail: boolean; +}): CommonResponseBody<{ + destroyCoreViewFilterGroup: boolean; +}> => { + const graphqlOperation = destroyCoreViewFilterGroupQueryFactory({ id }); + + const response = await makeMetadataAPIRequest(graphqlOperation); + + if (expectToFail === true) { + warnIfNoErrorButExpectedToFail({ + response, + errorMessage: + 'View Filter Group destruction should have failed but did not', + }); + } + + if (expectToFail === false) { + warnIfErrorButNotExpectedToFail({ + response, + errorMessage: 'View Filter Group destruction has failed but should not', + }); + } + + return { data: response.body.data, errors: response.body.errors }; +}; diff --git a/packages/twenty-server/test/integration/metadata/suites/view-filter-group/utils/update-core-view-filter-group-query-factory.util.ts b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/utils/update-core-view-filter-group-query-factory.util.ts new file mode 100644 index 0000000000..2ddd31b1c1 --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/utils/update-core-view-filter-group-query-factory.util.ts @@ -0,0 +1,25 @@ +import gql from 'graphql-tag'; +import { VIEW_FILTER_GROUP_GQL_FIELDS } from 'test/integration/constants/view-gql-fields.constants'; +import { type PerformMetadataQueryParams } from 'test/integration/metadata/types/perform-metadata-query.type'; + +import { type UpdateViewFilterGroupInput } from 'src/engine/metadata-modules/view-filter-group/dtos/inputs/update-view-filter-group.input'; + +export const updateCoreViewFilterGroupQueryFactory = ({ + gqlFields = VIEW_FILTER_GROUP_GQL_FIELDS, + input, + id, +}: PerformMetadataQueryParams & { + id: string; +}) => ({ + query: gql` + mutation UpdateCoreViewFilterGroup($id: String!, $input: UpdateViewFilterGroupInput!) { + updateCoreViewFilterGroup(id: $id, input: $input) { + ${gqlFields} + } + } + `, + variables: { + id, + input, + }, +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/view-filter-group/utils/update-one-core-view-filter-group.util.ts b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/utils/update-one-core-view-filter-group.util.ts new file mode 100644 index 0000000000..7ab741d0b7 --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/view-filter-group/utils/update-one-core-view-filter-group.util.ts @@ -0,0 +1,44 @@ +import { makeMetadataAPIRequest } from 'test/integration/metadata/suites/utils/make-metadata-api-request.util'; +import { updateCoreViewFilterGroupQueryFactory } from 'test/integration/metadata/suites/view-filter-group/utils/update-core-view-filter-group-query-factory.util'; +import { type CommonResponseBody } from 'test/integration/metadata/types/common-response-body.type'; +import { type PerformMetadataQueryParams } from 'test/integration/metadata/types/perform-metadata-query.type'; +import { warnIfErrorButNotExpectedToFail } from 'test/integration/metadata/utils/warn-if-error-but-not-expected-to-fail.util'; +import { warnIfNoErrorButExpectedToFail } from 'test/integration/metadata/utils/warn-if-no-error-but-expected-to-fail.util'; + +import { type UpdateViewFilterGroupInput } from 'src/engine/metadata-modules/view-filter-group/dtos/inputs/update-view-filter-group.input'; +import { type ViewFilterGroupDTO } from 'src/engine/metadata-modules/view-filter-group/dtos/view-filter-group.dto'; + +export const updateOneCoreViewFilterGroup = async ({ + id, + input, + gqlFields, + expectToFail, +}: PerformMetadataQueryParams & { + id: string; +}): CommonResponseBody<{ + updateCoreViewFilterGroup: ViewFilterGroupDTO; +}> => { + const graphqlOperation = updateCoreViewFilterGroupQueryFactory({ + id, + input, + gqlFields, + }); + + const response = await makeMetadataAPIRequest(graphqlOperation); + + if (expectToFail === true) { + warnIfNoErrorButExpectedToFail({ + response, + errorMessage: 'View Filter Group update should have failed but did not', + }); + } + + if (expectToFail === false) { + warnIfErrorButNotExpectedToFail({ + response, + errorMessage: 'View Filter Group update has failed but should not', + }); + } + + return { data: response.body.data, errors: response.body.errors }; +}; diff --git a/packages/twenty-server/test/integration/metadata/suites/view-filter/__snapshots__/failing-view-filter-creation.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/view-filter/__snapshots__/failing-view-filter-creation.integration-spec.ts.snap new file mode 100644 index 0000000000..8097ae1300 --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/view-filter/__snapshots__/failing-view-filter-creation.integration-spec.ts.snap @@ -0,0 +1,227 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`View Filter creation should fail when fieldMetadataId does not exist 1`] = ` +{ + "extensions": { + "code": "METADATA_VALIDATION_FAILED", + "errors": { + "agent": [], + "cronTrigger": [], + "databaseEventTrigger": [], + "fieldMetadata": [], + "index": [], + "objectMetadata": [], + "pageLayout": [], + "pageLayoutTab": [], + "pageLayoutWidget": [], + "role": [], + "roleTarget": [], + "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], + "serverlessFunction": [], + "view": [], + "viewField": [], + "viewFilter": [ + { + "errors": [ + { + "code": "INVALID_VIEW_FILTER_DATA", + "message": "Field metadata not found", + "userFriendlyMessage": "Field metadata not found", + }, + ], + "flatEntityMinimalInformation": { + "id": Any, + }, + "status": "fail", + "type": "create_view_filter", + }, + ], + "viewFilterGroup": [], + "viewGroup": [], + }, + "message": "Validation failed for 0 object(s) and 0 field(s)", + "summary": { + "invalidAgent": 0, + "invalidCronTrigger": 0, + "invalidDatabaseEventTrigger": 0, + "invalidFieldMetadata": 0, + "invalidIndex": 0, + "invalidObjectMetadata": 0, + "invalidPageLayout": 0, + "invalidPageLayoutTab": 0, + "invalidPageLayoutWidget": 0, + "invalidRole": 0, + "invalidRoleTarget": 0, + "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, + "invalidServerlessFunction": 0, + "invalidView": 0, + "invalidViewField": 0, + "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, + "invalidViewGroup": 0, + "totalErrors": 0, + }, + "userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)", + }, + "message": "Multiple validation errors occurred while creating view filter", + "name": "GraphQLError", +} +`; + +exports[`View Filter creation should fail when viewFilterGroupId does not exist 1`] = ` +{ + "extensions": { + "code": "METADATA_VALIDATION_FAILED", + "errors": { + "agent": [], + "cronTrigger": [], + "databaseEventTrigger": [], + "fieldMetadata": [], + "index": [], + "objectMetadata": [], + "pageLayout": [], + "pageLayoutTab": [], + "pageLayoutWidget": [], + "role": [], + "roleTarget": [], + "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], + "serverlessFunction": [], + "view": [], + "viewField": [], + "viewFilter": [ + { + "errors": [ + { + "code": "INVALID_VIEW_FILTER_DATA", + "message": "Field metadata not found", + "userFriendlyMessage": "Field metadata not found", + }, + { + "code": "INVALID_VIEW_FILTER_DATA", + "message": "View filter group not found", + "userFriendlyMessage": "View filter group not found", + }, + ], + "flatEntityMinimalInformation": { + "id": Any, + }, + "status": "fail", + "type": "create_view_filter", + }, + ], + "viewFilterGroup": [], + "viewGroup": [], + }, + "message": "Validation failed for 0 object(s) and 0 field(s)", + "summary": { + "invalidAgent": 0, + "invalidCronTrigger": 0, + "invalidDatabaseEventTrigger": 0, + "invalidFieldMetadata": 0, + "invalidIndex": 0, + "invalidObjectMetadata": 0, + "invalidPageLayout": 0, + "invalidPageLayoutTab": 0, + "invalidPageLayoutWidget": 0, + "invalidRole": 0, + "invalidRoleTarget": 0, + "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, + "invalidServerlessFunction": 0, + "invalidView": 0, + "invalidViewField": 0, + "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, + "invalidViewGroup": 0, + "totalErrors": 0, + }, + "userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)", + }, + "message": "Multiple validation errors occurred while creating view filter", + "name": "GraphQLError", +} +`; + +exports[`View Filter creation should fail when viewId does not exist 1`] = ` +{ + "extensions": { + "code": "METADATA_VALIDATION_FAILED", + "errors": { + "agent": [], + "cronTrigger": [], + "databaseEventTrigger": [], + "fieldMetadata": [], + "index": [], + "objectMetadata": [], + "pageLayout": [], + "pageLayoutTab": [], + "pageLayoutWidget": [], + "role": [], + "roleTarget": [], + "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], + "serverlessFunction": [], + "view": [], + "viewField": [], + "viewFilter": [ + { + "errors": [ + { + "code": "INVALID_VIEW_FILTER_DATA", + "message": "View not found", + "userFriendlyMessage": "View not found", + }, + { + "code": "INVALID_VIEW_FILTER_DATA", + "message": "Field metadata not found", + "userFriendlyMessage": "Field metadata not found", + }, + ], + "flatEntityMinimalInformation": { + "id": Any, + }, + "status": "fail", + "type": "create_view_filter", + }, + ], + "viewFilterGroup": [], + "viewGroup": [], + }, + "message": "Validation failed for 0 object(s) and 0 field(s)", + "summary": { + "invalidAgent": 0, + "invalidCronTrigger": 0, + "invalidDatabaseEventTrigger": 0, + "invalidFieldMetadata": 0, + "invalidIndex": 0, + "invalidObjectMetadata": 0, + "invalidPageLayout": 0, + "invalidPageLayoutTab": 0, + "invalidPageLayoutWidget": 0, + "invalidRole": 0, + "invalidRoleTarget": 0, + "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, + "invalidServerlessFunction": 0, + "invalidView": 0, + "invalidViewField": 0, + "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, + "invalidViewGroup": 0, + "totalErrors": 0, + }, + "userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)", + }, + "message": "Multiple validation errors occurred while creating view filter", + "name": "GraphQLError", +} +`; diff --git a/packages/twenty-server/test/integration/metadata/suites/view-filter/failing-view-filter-creation.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/view-filter/failing-view-filter-creation.integration-spec.ts new file mode 100644 index 0000000000..4a6ba4a64f --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/view-filter/failing-view-filter-creation.integration-spec.ts @@ -0,0 +1,122 @@ +import { expectOneNotInternalServerErrorSnapshot } from 'test/integration/graphql/utils/expect-one-not-internal-server-error-snapshot.util'; +import { findManyObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/find-many-object-metadata.util'; +import { createOneCoreViewFilter } from 'test/integration/metadata/suites/view-filter/utils/create-one-core-view-filter.util'; +import { createOneCoreView } from 'test/integration/metadata/suites/view/utils/create-one-core-view.util'; +import { destroyOneCoreView } from 'test/integration/metadata/suites/view/utils/destroy-one-core-view.util'; +import { jestExpectToBeDefined } from 'test/utils/jest-expect-to-be-defined.util.test'; +import { + eachTestingContextFilter, + type EachTestingContext, +} from 'twenty-shared/testing'; +import { ViewFilterOperand } from 'twenty-shared/types'; +import { v4 } from 'uuid'; + +import { type CreateViewFilterInput } from 'src/engine/metadata-modules/view-filter/dtos/inputs/create-view-filter.input'; +import { ViewType } from 'src/engine/metadata-modules/view/enums/view-type.enum'; + +type TestSetup = { + createdViewId: string; +}; + +type TestContext = { + input: (testSetup: TestSetup) => CreateViewFilterInput; +}; + +const failingViewFilterCreationTestCases: EachTestingContext[] = [ + { + title: 'when viewId does not exist', + context: { + input: () => ({ + viewId: v4(), + fieldMetadataId: v4(), + value: 'test', + operand: ViewFilterOperand.CONTAINS, + }), + }, + }, + { + title: 'when fieldMetadataId does not exist', + context: { + input: (testSetup) => ({ + viewId: testSetup.createdViewId, + fieldMetadataId: v4(), + value: 'test', + operand: ViewFilterOperand.CONTAINS, + }), + }, + }, + { + title: 'when viewFilterGroupId does not exist', + context: { + input: (testSetup) => ({ + viewId: testSetup.createdViewId, + fieldMetadataId: v4(), + value: 'test', + operand: ViewFilterOperand.CONTAINS, + viewFilterGroupId: v4(), + }), + }, + }, +]; + +describe('View Filter creation should fail', () => { + let createdViewId: string; + + beforeAll(async () => { + const { objects } = await findManyObjectMetadata({ + expectToFail: false, + input: { + filter: {}, + paging: { first: 1000 }, + }, + gqlFields: ` + id + nameSingular + `, + }); + + jestExpectToBeDefined(objects); + + const companyObjectMetadata = objects.find( + (object: { nameSingular: string }) => object.nameSingular === 'company', + ); + + jestExpectToBeDefined(companyObjectMetadata); + + const { data: viewData } = await createOneCoreView({ + expectToFail: false, + input: { + name: 'Test View For Failing Filter Creation', + objectMetadataId: companyObjectMetadata.id, + type: ViewType.TABLE, + icon: 'IconBuildingSkyscraper', + }, + }); + + createdViewId = viewData?.createCoreView?.id; + jestExpectToBeDefined(createdViewId); + }); + + afterAll(async () => { + if (createdViewId) { + await destroyOneCoreView({ + expectToFail: false, + viewId: createdViewId, + }); + } + }); + + it.each(eachTestingContextFilter(failingViewFilterCreationTestCases))( + '$title', + async ({ context }) => { + const { errors } = await createOneCoreViewFilter({ + expectToFail: true, + input: context.input({ createdViewId }), + }); + + expectOneNotInternalServerErrorSnapshot({ + errors, + }); + }, + ); +}); diff --git a/packages/twenty-server/test/integration/rest/suites/__snapshots__/view-group.integration-spec.ts.snap b/packages/twenty-server/test/integration/rest/suites/__snapshots__/view-group.integration-spec.ts.snap index 4373d2bb73..ff003f0738 100644 --- a/packages/twenty-server/test/integration/rest/suites/__snapshots__/view-group.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/rest/suites/__snapshots__/view-group.integration-spec.ts.snap @@ -22,6 +22,7 @@ exports[`View Group REST API POST /metadata/viewGroups should fail to create vie "view": [], "viewField": [], "viewFilter": [], + "viewFilterGroup": [], "viewGroup": [ { "errors": [ @@ -61,6 +62,7 @@ exports[`View Group REST API POST /metadata/viewGroups should fail to create vie "invalidView": 0, "invalidViewField": 0, "invalidViewFilter": 0, + "invalidViewFilterGroup": 0, "invalidViewGroup": 0, "totalErrors": 0, }, diff --git a/packages/twenty-shared/src/metadata/all-metadata-name.constant.ts b/packages/twenty-shared/src/metadata/all-metadata-name.constant.ts index b114d5ffbd..c7135bd3b7 100644 --- a/packages/twenty-shared/src/metadata/all-metadata-name.constant.ts +++ b/packages/twenty-shared/src/metadata/all-metadata-name.constant.ts @@ -6,6 +6,7 @@ export const ALL_METADATA_NAME = { viewGroup: 'viewGroup', rowLevelPermissionPredicate: 'rowLevelPermissionPredicate', rowLevelPermissionPredicateGroup: 'rowLevelPermissionPredicateGroup', + viewFilterGroup: 'viewFilterGroup', index: 'index', serverlessFunction: 'serverlessFunction', cronTrigger: 'cronTrigger',