From 15b21570ae9ef0bdfcd4232146698bd4a9dbea78 Mon Sep 17 00:00:00 2001 From: Weiko Date: Wed, 31 Dec 2025 16:15:17 +0100 Subject: [PATCH] Row level permissions - POC 1 (#16599) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Context This PR adds the core structure for RLS implementation: - RLS data model - RLS service layer - RLS WorkspaceMigration and Syncable Entity + cache + Validations - RLS resolver layer - ORM layer with RLS Predicate to ORM WHERE clause conversion with workspaceMember record transposition Tests are missing though --- > [!NOTE] > Establishes core row-level permissions infrastructure and enforcement across the stack. > > - Backend: new `rowLevelPermissionPredicate` and `rowLevelPermissionPredicateGroup` entities, TypeORM migration, feature flag `IS_ROW_LEVEL_PERMISSION_PREDICATES_ENABLED`, flat-entity maps/cache wiring, services and GraphQL resolvers for CRUD, and inclusion of `workspaceMember` in auth context > - ORM: applies row-level permission predicates to SELECT, DELETE, and SOFT DELETE query builders; propagates context through GlobalWorkspaceOrmManager/EntityManager > - GraphQL: generated schema/types/queries/mutations for creating/updating/deleting/fetching predicates and groups > - Frontend: settings page adds a gated "Record-level" section (placeholder) and metadata error handler labels for new entities > > Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit fe955cc4588a92157afa6795fb574189a4be1e93. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot). --------- Co-authored-by: Félix Malfait --- .../src/generated-metadata/graphql.ts | 150 +++++++++ .../twenty-front/src/generated/graphql.ts | 150 +++++++++ .../hooks/useMetadataErrorHandler.ts | 2 + ...gsRolePermissionsObjectLevelObjectForm.tsx | 14 +- ...rmissionsObjectLevelRecordLevelSection.tsx | 21 ++ packages/twenty-server/@types/express.d.ts | 1 + .../migrations/common/1765499361805-addRLS.ts | 131 ++++++++ .../workspace-auth-context.interface.ts | 1 + .../utils/create-query-runner-context.util.ts | 2 + .../auth/strategies/jwt.auth.strategy.spec.ts | 38 ++- .../auth/strategies/jwt.auth.strategy.ts | 63 +++- .../auth/types/auth-context.type.ts | 4 +- .../engine/core-modules/core-engine.module.ts | 3 + .../enums/feature-flag-key.enum.ts | 1 + ...rties-to-compare-and-stringify.constant.ts | 16 + ...metadata-many-to-one-relations.constant.ts | 2 + ...-metadata-relations-properties.constant.ts | 17 + ...quired-metadata-for-validation.constant.ts | 9 + ...ny-or-all-flat-entity-maps-cache.module.ts | 10 + .../all-flat-entity-types-by-metadata-name.ts | 49 ++- ...cate-group-editable-properties.constant.ts | 9 + ...rmission-predicate-group-to-create.util.ts | 55 ++++ ...ermission-predicate-group-or-throw.util.ts | 45 +++ ...ermission-predicate-group-or-throw.util.ts | 42 +++ ...-permission-predicate-group-to-dto.util.ts | 10 + ...predicate-group-to-update-or-throw.util.ts | 55 ++++ ...-predicate-editable-properties.constant.ts | 13 + ...ate-entity-relation-properties.constant.ts | 11 + ...oup-entity-relation-properties.constant.ts | 10 + ...ssion-predicate-group-map-cache.service.ts | 55 ++++ ...-permission-predicate-map-cache.service.ts | 54 ++++ ...vel-permission-predicate-to-create.util.ts | 72 +++++ ...evel-permission-predicate-or-throw.util.ts | 45 +++ ...evel-permission-predicate-or-throw.util.ts | 42 +++ ...-level-permission-predicate-to-dto.util.ts | 10 + ...lat-row-level-permission-predicate.util.ts | 28 ++ ...w-level-permission-predicate-group.util.ts | 36 +++ ...ssion-predicate-to-update-or-throw.util.ts | 55 ++++ ...-level-permission-predicate-group.input.ts | 20 ++ ...te-row-level-permission-predicate.input.ts | 41 +++ ...-level-permission-predicate-group.input.ts | 9 + ...te-row-level-permission-predicate.input.ts | 9 + ...-level-permission-predicate-group.input.ts | 18 ++ ...oy-row-level-permission-predicate.input.ts | 17 + ...-level-permission-predicate-group.input.ts | 22 ++ ...te-row-level-permission-predicate.input.ts | 35 ++ ...ow-level-permission-predicate-group.dto.ts | 26 ++ .../row-level-permission-predicate.dto.ts | 47 +++ ...level-permission-predicate-group.entity.ts | 82 +++++ .../row-level-permission-predicate.entity.ts | 115 +++++++ ...n-predicate-group-logical-operator.enum.ts | 6 + .../row-level-permission-predicate-operand.ts | 20 ++ ...el-permission-predicate-group.exception.ts | 44 +++ ...ow-level-permission-predicate.exception.ts | 48 +++ ...vel-permission-predicate-group.resolver.ts | 81 +++++ ...row-level-permission-predicate.resolver.ts | 95 ++++++ .../row-level-permission.module.ts | 37 +++ ...evel-permission-predicate-group.service.ts | 298 +++++++++++++++++ .../row-level-permission-predicate.service.ts | 304 ++++++++++++++++++ ...el-permission-predicate-group-maps.type.ts | 7 + ...w-level-permission-predicate-group.type.ts | 10 + ...ow-level-permission-predicate-maps.type.ts | 7 + ...lat-row-level-permission-predicate.type.ts | 7 + ...w-level-permission-predicate-value.type.ts | 16 + .../engine/middlewares/middleware.service.ts | 2 +- .../workspace-entity-manager.spec.ts | 17 + .../workspace-entity-manager.ts | 14 +- .../global-workspace-orm.manager.ts | 6 + .../workspace-internal-context.interface.ts | 5 + .../__tests__/workspace.repository.spec.ts | 13 +- .../workspace-delete-query-builder.ts | 27 +- .../workspace-select-query-builder.ts | 37 ++- .../workspace-soft-delete-query-builder.ts | 27 +- .../workspace-update-query-builder.ts | 31 +- .../storage/orm-workspace-context.storage.ts | 4 + ...ly-row-level-permission-predicates.util.ts | 220 +++++++++++++ ...row-level-permission-record-filter.util.ts | 201 ++++++++++++ .../utils/bind-data-to-request-object.util.ts | 1 + .../types/workspace-cache-key.type.ts | 8 + ...metadata-validation-response-error.util.ts | 2 + ...ce-migration-build-orchestrator.service.ts | 104 ++++++ ...rmission-predicate-group-action-v2.type.ts | 28 ++ ...predicate-group-actions-builder.service.ts | 121 +++++++ ...vel-permission-predicate-action-v2.type.ts | 34 ++ ...ssion-predicate-actions-builder.service.ts | 121 +++++++ ...ssion-predicate-group-validator.service.ts | 209 ++++++++++++ ...-permission-predicate-validator.service.ts | 272 ++++++++++++++++ ...ace-migration-builder-validators.module.ts | 8 +- .../workspace-migration-builder-v2.module.ts | 8 +- ...-predicate-group-action-handler.service.ts | 59 ++++ ...-predicate-group-action-handler.service.ts | 59 ++++ ...-predicate-group-action-handler.service.ts | 70 ++++ ...ission-predicate-action-handler.service.ts | 59 ++++ ...ission-predicate-action-handler.service.ts | 59 ++++ ...ission-predicate-action-handler.service.ts | 70 ++++ ...migration-runner-action-handlers.module.ts | 20 +- ...ny-view-fields-v2.integration-spec.ts.snap | 4 + ...ate-view-field-v2.integration-spec.ts.snap | 12 + ...ect-on-view-field.integration-spec.ts.snap | 20 ++ ...ng-agent-creation.integration-spec.ts.snap | 28 ++ ...ng-agent-deletion.integration-spec.ts.snap | 4 + ...ling-agent-update.integration-spec.ts.snap | 36 +++ ...ne-field-metadata.integration-spec.ts.snap | 4 + ...ne-field-metadata.integration-spec.ts.snap | 12 + ...ne-field-metadata.integration-spec.ts.snap | 8 + ...-metadata-enum-v2.integration-spec.ts.snap | 280 ++++++++++++++++ ...field-metadata-v2.integration-spec.ts.snap | 272 ++++++++++++++++ ...morph-relation-v2.integration-spec.ts.snap | 8 + ...morph-relation-v2.integration-spec.ts.snap | 68 ++++ ...relation-creation.integration-spec.ts.snap | 96 ++++++ ...a-relation-update.integration-spec.ts.snap | 4 + ...ue-field-metadata.integration-spec.ts.snap | 8 + ...bject-metadata-v2.integration-spec.ts.snap | 68 ++++ ...e-object-metadata.integration-spec.ts.snap | 16 + ...yout-tab-creation.integration-spec.ts.snap | 4 + ...t-widget-creation.integration-spec.ts.snap | 4 + ...ing-role-creation.integration-spec.ts.snap | 20 ++ ...iling-role-update.integration-spec.ts.snap | 28 ++ .../view-group.integration-spec.ts.snap | 4 + .../metadata/all-metadata-name.constant.ts | 2 + 120 files changed, 5649 insertions(+), 29 deletions(-) create mode 100644 packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/record-level-permissions/components/SettingsRolePermissionsObjectLevelRecordLevelSection.tsx create mode 100644 packages/twenty-server/src/database/typeorm/core/migrations/common/1765499361805-addRLS.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate-group/constants/flat-row-level-permission-predicate-group-editable-properties.constant.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate-group/utils/from-create-row-level-permission-predicate-group-input-to-flat-row-level-permission-predicate-group-to-create.util.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate-group/utils/from-delete-row-level-permission-predicate-group-input-to-flat-row-level-permission-predicate-group-or-throw.util.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate-group/utils/from-destroy-row-level-permission-predicate-group-input-to-flat-row-level-permission-predicate-group-or-throw.util.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate-group/utils/from-flat-row-level-permission-predicate-group-to-dto.util.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate-group/utils/from-update-row-level-permission-predicate-group-input-to-flat-row-level-permission-predicate-group-to-update-or-throw.util.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/constants/flat-row-level-permission-predicate-editable-properties.constant.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/constants/row-level-permission-predicate-entity-relation-properties.constant.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/constants/row-level-permission-predicate-group-entity-relation-properties.constant.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/services/workspace-flat-row-level-permission-predicate-group-map-cache.service.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/services/workspace-flat-row-level-permission-predicate-map-cache.service.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-create-row-level-permission-predicate-input-to-flat-row-level-permission-predicate-to-create.util.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-delete-row-level-permission-predicate-input-to-flat-row-level-permission-predicate-or-throw.util.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-destroy-row-level-permission-predicate-input-to-flat-row-level-permission-predicate-or-throw.util.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-flat-row-level-permission-predicate-to-dto.util.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-row-level-permission-predicate-entity-to-flat-row-level-permission-predicate.util.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-row-level-permission-predicate-group-entity-to-flat-row-level-permission-predicate-group.util.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-update-row-level-permission-predicate-input-to-flat-row-level-permission-predicate-to-update-or-throw.util.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/create-row-level-permission-predicate-group.input.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/create-row-level-permission-predicate.input.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/delete-row-level-permission-predicate-group.input.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/delete-row-level-permission-predicate.input.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/destroy-row-level-permission-predicate-group.input.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/destroy-row-level-permission-predicate.input.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/update-row-level-permission-predicate-group.input.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/update-row-level-permission-predicate.input.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/row-level-permission-predicate-group.dto.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/row-level-permission-predicate.dto.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate-group.entity.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate.entity.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/enums/row-level-permission-predicate-group-logical-operator.enum.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/enums/row-level-permission-predicate-operand.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/exceptions/row-level-permission-predicate-group.exception.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/exceptions/row-level-permission-predicate.exception.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/resolvers/row-level-permission-predicate-group.resolver.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/resolvers/row-level-permission-predicate.resolver.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/row-level-permission.module.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/services/row-level-permission-predicate-group.service.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/services/row-level-permission-predicate.service.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-group-maps.type.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-group.type.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-maps.type.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate.type.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/types/row-level-permission-predicate-value.type.ts create mode 100644 packages/twenty-server/src/engine/twenty-orm/utils/apply-row-level-permission-predicates.util.ts create mode 100644 packages/twenty-server/src/engine/twenty-orm/utils/build-row-level-permission-record-filter.util.ts create mode 100644 packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/row-level-permission-predicate-group/types/workspace-migration-row-level-permission-predicate-group-action-v2.type.ts create mode 100644 packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/row-level-permission-predicate-group/workspace-migration-v2-row-level-permission-predicate-group-actions-builder.service.ts create mode 100644 packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/row-level-permission-predicate/types/workspace-migration-row-level-permission-predicate-action-v2.type.ts create mode 100644 packages/twenty-server/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.ts create mode 100644 packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-row-level-permission-predicate-group-validator.service.ts create mode 100644 packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-row-level-permission-predicate-validator.service.ts create mode 100644 packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/row-level-permission-predicate-group/services/create-row-level-permission-predicate-group-action-handler.service.ts create mode 100644 packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/row-level-permission-predicate-group/services/delete-row-level-permission-predicate-group-action-handler.service.ts create mode 100644 packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/row-level-permission-predicate-group/services/update-row-level-permission-predicate-group-action-handler.service.ts create mode 100644 packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/row-level-permission-predicate/services/create-row-level-permission-predicate-action-handler.service.ts create mode 100644 packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/row-level-permission-predicate/services/delete-row-level-permission-predicate-action-handler.service.ts create mode 100644 packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/row-level-permission-predicate/services/update-row-level-permission-predicate-action-handler.service.ts diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 01093fd44a..55a023e40b 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -943,6 +943,26 @@ export type CreateRouteTriggerInput = { serverlessFunctionId: Scalars['String']; }; +export type CreateRowLevelPermissionPredicateGroupInput = { + logicalOperator: RowLevelPermissionPredicateGroupLogicalOperator; + parentRowLevelPermissionPredicateGroupId?: InputMaybe; + positionInRowLevelPermissionPredicateGroup?: InputMaybe; + roleId: Scalars['String']; +}; + +export type CreateRowLevelPermissionPredicateInput = { + fieldMetadataId: Scalars['String']; + objectMetadataId: Scalars['String']; + operand: RowLevelPermissionPredicateOperand; + positionInRowLevelPermissionPredicateGroup?: InputMaybe; + roleId: Scalars['String']; + rowLevelPermissionPredicateGroupId?: InputMaybe; + subFieldName?: InputMaybe; + value?: InputMaybe; + workspaceMemberFieldMetadataId?: InputMaybe; + workspaceMemberSubFieldName?: InputMaybe; +}; + export type CreateServerlessFunctionInput = { code?: InputMaybe; description?: InputMaybe; @@ -1127,6 +1147,14 @@ export type DeleteOneObjectInput = { id: Scalars['UUID']; }; +export type DeleteRowLevelPermissionPredicateGroupInput = { + id: Scalars['String']; +}; + +export type DeleteRowLevelPermissionPredicateInput = { + id: Scalars['String']; +}; + export type DeleteSsoInput = { identityProviderId: Scalars['UUID']; }; @@ -1321,6 +1349,7 @@ export enum FeatureFlagKey { IS_POSTGRESQL_INTEGRATION_ENABLED = 'IS_POSTGRESQL_INTEGRATION_ENABLED', IS_PUBLIC_DOMAIN_ENABLED = 'IS_PUBLIC_DOMAIN_ENABLED', IS_RECORD_PAGE_LAYOUT_ENABLED = 'IS_RECORD_PAGE_LAYOUT_ENABLED', + IS_ROW_LEVEL_PERMISSION_PREDICATES_ENABLED = 'IS_ROW_LEVEL_PERMISSION_PREDICATES_ENABLED', IS_STRIPE_INTEGRATION_ENABLED = 'IS_STRIPE_INTEGRATION_ENABLED', IS_TIMELINE_ACTIVITY_MIGRATED = 'IS_TIMELINE_ACTIVITY_MIGRATED', IS_UNIQUE_INDEXES_ENABLED = 'IS_UNIQUE_INDEXES_ENABLED', @@ -1822,6 +1851,8 @@ export type Mutation = { createPageLayoutTab: PageLayoutTab; createPageLayoutWidget: PageLayoutWidget; createPublicDomain: PublicDomain; + createRowLevelPermissionPredicate: RowLevelPermissionPredicate; + createRowLevelPermissionPredicateGroup: RowLevelPermissionPredicateGroup; createSAMLIdentityProvider: SetupSsoOutput; createWebhook: Webhook; createWorkflowVersionEdge: WorkflowVersionStepChanges; @@ -1852,6 +1883,8 @@ export type Mutation = { deletePageLayoutTab: Scalars['Boolean']; deletePageLayoutWidget: PageLayoutWidget; deletePublicDomain: Scalars['Boolean']; + deleteRowLevelPermissionPredicate: RowLevelPermissionPredicate; + deleteRowLevelPermissionPredicateGroup: RowLevelPermissionPredicateGroup; deleteSSOIdentityProvider: DeleteSsoOutput; deleteTwoFactorAuthenticationMethod: DeleteTwoFactorAuthenticationMethodOutput; deleteUser: User; @@ -1946,6 +1979,8 @@ export type Mutation = { updatePageLayoutWidget: PageLayoutWidget; updatePageLayoutWithTabsAndWidgets: PageLayout; updatePasswordViaResetToken: InvalidatePasswordOutput; + updateRowLevelPermissionPredicate: RowLevelPermissionPredicate; + updateRowLevelPermissionPredicateGroup: RowLevelPermissionPredicateGroup; updateUserEmail: Scalars['Boolean']; updateWebhook?: Maybe; updateWorkflowRunStep: WorkflowAction; @@ -2178,6 +2213,16 @@ export type MutationCreatePublicDomainArgs = { }; +export type MutationCreateRowLevelPermissionPredicateArgs = { + input: CreateRowLevelPermissionPredicateInput; +}; + + +export type MutationCreateRowLevelPermissionPredicateGroupArgs = { + input: CreateRowLevelPermissionPredicateGroupInput; +}; + + export type MutationCreateSamlIdentityProviderArgs = { input: SetupSamlSsoInput; }; @@ -2324,6 +2369,16 @@ export type MutationDeletePublicDomainArgs = { }; +export type MutationDeleteRowLevelPermissionPredicateArgs = { + input: DeleteRowLevelPermissionPredicateInput; +}; + + +export type MutationDeleteRowLevelPermissionPredicateGroupArgs = { + input: DeleteRowLevelPermissionPredicateGroupInput; +}; + + export type MutationDeleteSsoIdentityProviderArgs = { input: DeleteSsoInput; }; @@ -2789,6 +2844,16 @@ export type MutationUpdatePasswordViaResetTokenArgs = { }; +export type MutationUpdateRowLevelPermissionPredicateArgs = { + input: UpdateRowLevelPermissionPredicateInput; +}; + + +export type MutationUpdateRowLevelPermissionPredicateGroupArgs = { + input: UpdateRowLevelPermissionPredicateGroupInput; +}; + + export type MutationUpdateUserEmailArgs = { newEmail: Scalars['String']; verifyEmailRedirectPath?: InputMaybe; @@ -3307,6 +3372,9 @@ export type Query = { getQueueJobs: QueueJobsResponse; getQueueMetrics: QueueMetricsData; getRoles: Array; + getRowLevelPermissionPredicate?: Maybe; + getRowLevelPermissionPredicateGroups: Array; + getRowLevelPermissionPredicates: Array; getSSOIdentityProviders: Array; getServerlessFunctionSourceCode?: Maybe; getSystemHealthStatus: SystemHealth; @@ -3569,6 +3637,22 @@ export type QueryGetQueueMetricsArgs = { }; +export type QueryGetRowLevelPermissionPredicateArgs = { + id: Scalars['String']; +}; + + +export type QueryGetRowLevelPermissionPredicateGroupsArgs = { + roleId?: InputMaybe; +}; + + +export type QueryGetRowLevelPermissionPredicatesArgs = { + objectMetadataId?: InputMaybe; + roleId?: InputMaybe; +}; + + export type QueryGetServerlessFunctionSourceCodeArgs = { input: GetServerlessFunctionSourceCodeInput; }; @@ -3843,6 +3927,54 @@ export type RouteTriggerIdInput = { id: Scalars['String']; }; +export type RowLevelPermissionPredicate = { + __typename?: 'RowLevelPermissionPredicate'; + fieldMetadataId: Scalars['String']; + id: Scalars['String']; + objectMetadataId: Scalars['String']; + operand: RowLevelPermissionPredicateOperand; + positionInRowLevelPermissionPredicateGroup?: Maybe; + roleId: Scalars['String']; + rowLevelPermissionPredicateGroupId?: Maybe; + subFieldName?: Maybe; + value?: Maybe; + workspaceMemberFieldMetadataId?: Maybe; + workspaceMemberSubFieldName?: Maybe; +}; + +export type RowLevelPermissionPredicateGroup = { + __typename?: 'RowLevelPermissionPredicateGroup'; + id: Scalars['String']; + logicalOperator: RowLevelPermissionPredicateGroupLogicalOperator; + parentRowLevelPermissionPredicateGroupId?: Maybe; + positionInRowLevelPermissionPredicateGroup?: Maybe; + roleId: Scalars['String']; +}; + +export enum RowLevelPermissionPredicateGroupLogicalOperator { + AND = 'AND', + OR = 'OR' +} + +export enum RowLevelPermissionPredicateOperand { + CONTAINS = 'CONTAINS', + DOES_NOT_CONTAIN = 'DOES_NOT_CONTAIN', + GREATER_THAN_OR_EQUAL = 'GREATER_THAN_OR_EQUAL', + IS = 'IS', + IS_AFTER = 'IS_AFTER', + IS_BEFORE = 'IS_BEFORE', + IS_EMPTY = 'IS_EMPTY', + IS_IN_FUTURE = 'IS_IN_FUTURE', + IS_IN_PAST = 'IS_IN_PAST', + IS_NOT = 'IS_NOT', + IS_NOT_EMPTY = 'IS_NOT_EMPTY', + IS_NOT_NULL = 'IS_NOT_NULL', + IS_RELATIVE = 'IS_RELATIVE', + IS_TODAY = 'IS_TODAY', + LESS_THAN_OR_EQUAL = 'LESS_THAN_OR_EQUAL', + VECTOR_SEARCH = 'VECTOR_SEARCH' +} + export type RunWorkflowVersionInput = { /** Execution result in JSON format */ payload?: InputMaybe; @@ -4437,6 +4569,24 @@ export type UpdateRouteTriggerInputUpdates = { path: Scalars['String']; }; +export type UpdateRowLevelPermissionPredicateGroupInput = { + id: Scalars['String']; + logicalOperator?: InputMaybe; + parentRowLevelPermissionPredicateGroupId?: InputMaybe; + positionInRowLevelPermissionPredicateGroup?: InputMaybe; +}; + +export type UpdateRowLevelPermissionPredicateInput = { + id: Scalars['String']; + operand?: InputMaybe; + positionInRowLevelPermissionPredicateGroup?: InputMaybe; + rowLevelPermissionPredicateGroupId?: InputMaybe; + subFieldName?: InputMaybe; + value?: InputMaybe; + workspaceMemberFieldMetadataId?: InputMaybe; + workspaceMemberSubFieldName?: InputMaybe; +}; + export type UpdateServerlessFunctionInput = { /** Id of the serverless function to update */ id: Scalars['UUID']; diff --git a/packages/twenty-front/src/generated/graphql.ts b/packages/twenty-front/src/generated/graphql.ts index c2dcfa75b5..ff30c5e72a 100644 --- a/packages/twenty-front/src/generated/graphql.ts +++ b/packages/twenty-front/src/generated/graphql.ts @@ -926,6 +926,26 @@ export type CreateRouteTriggerInput = { serverlessFunctionId: Scalars['String']; }; +export type CreateRowLevelPermissionPredicateGroupInput = { + logicalOperator: RowLevelPermissionPredicateGroupLogicalOperator; + parentRowLevelPermissionPredicateGroupId?: InputMaybe; + positionInRowLevelPermissionPredicateGroup?: InputMaybe; + roleId: Scalars['String']; +}; + +export type CreateRowLevelPermissionPredicateInput = { + fieldMetadataId: Scalars['String']; + objectMetadataId: Scalars['String']; + operand: RowLevelPermissionPredicateOperand; + positionInRowLevelPermissionPredicateGroup?: InputMaybe; + roleId: Scalars['String']; + rowLevelPermissionPredicateGroupId?: InputMaybe; + subFieldName?: InputMaybe; + value?: InputMaybe; + workspaceMemberFieldMetadataId?: InputMaybe; + workspaceMemberSubFieldName?: InputMaybe; +}; + export type CreateServerlessFunctionInput = { code?: InputMaybe; description?: InputMaybe; @@ -1110,6 +1130,14 @@ export type DeleteOneObjectInput = { id: Scalars['UUID']; }; +export type DeleteRowLevelPermissionPredicateGroupInput = { + id: Scalars['String']; +}; + +export type DeleteRowLevelPermissionPredicateInput = { + id: Scalars['String']; +}; + export type DeleteSsoInput = { identityProviderId: Scalars['UUID']; }; @@ -1304,6 +1332,7 @@ export enum FeatureFlagKey { IS_POSTGRESQL_INTEGRATION_ENABLED = 'IS_POSTGRESQL_INTEGRATION_ENABLED', IS_PUBLIC_DOMAIN_ENABLED = 'IS_PUBLIC_DOMAIN_ENABLED', IS_RECORD_PAGE_LAYOUT_ENABLED = 'IS_RECORD_PAGE_LAYOUT_ENABLED', + IS_ROW_LEVEL_PERMISSION_PREDICATES_ENABLED = 'IS_ROW_LEVEL_PERMISSION_PREDICATES_ENABLED', IS_STRIPE_INTEGRATION_ENABLED = 'IS_STRIPE_INTEGRATION_ENABLED', IS_TIMELINE_ACTIVITY_MIGRATED = 'IS_TIMELINE_ACTIVITY_MIGRATED', IS_UNIQUE_INDEXES_ENABLED = 'IS_UNIQUE_INDEXES_ENABLED', @@ -1796,6 +1825,8 @@ export type Mutation = { createPageLayoutTab: PageLayoutTab; createPageLayoutWidget: PageLayoutWidget; createPublicDomain: PublicDomain; + createRowLevelPermissionPredicate: RowLevelPermissionPredicate; + createRowLevelPermissionPredicateGroup: RowLevelPermissionPredicateGroup; createSAMLIdentityProvider: SetupSsoOutput; createWebhook: Webhook; createWorkflowVersionEdge: WorkflowVersionStepChanges; @@ -1825,6 +1856,8 @@ export type Mutation = { deletePageLayoutTab: Scalars['Boolean']; deletePageLayoutWidget: PageLayoutWidget; deletePublicDomain: Scalars['Boolean']; + deleteRowLevelPermissionPredicate: RowLevelPermissionPredicate; + deleteRowLevelPermissionPredicateGroup: RowLevelPermissionPredicateGroup; deleteSSOIdentityProvider: DeleteSsoOutput; deleteTwoFactorAuthenticationMethod: DeleteTwoFactorAuthenticationMethodOutput; deleteUser: User; @@ -1913,6 +1946,8 @@ export type Mutation = { updatePageLayoutWidget: PageLayoutWidget; updatePageLayoutWithTabsAndWidgets: PageLayout; updatePasswordViaResetToken: InvalidatePasswordOutput; + updateRowLevelPermissionPredicate: RowLevelPermissionPredicate; + updateRowLevelPermissionPredicateGroup: RowLevelPermissionPredicateGroup; updateUserEmail: Scalars['Boolean']; updateWebhook?: Maybe; updateWorkflowRunStep: WorkflowAction; @@ -2135,6 +2170,16 @@ export type MutationCreatePublicDomainArgs = { }; +export type MutationCreateRowLevelPermissionPredicateArgs = { + input: CreateRowLevelPermissionPredicateInput; +}; + + +export type MutationCreateRowLevelPermissionPredicateGroupArgs = { + input: CreateRowLevelPermissionPredicateGroupInput; +}; + + export type MutationCreateSamlIdentityProviderArgs = { input: SetupSamlSsoInput; }; @@ -2276,6 +2321,16 @@ export type MutationDeletePublicDomainArgs = { }; +export type MutationDeleteRowLevelPermissionPredicateArgs = { + input: DeleteRowLevelPermissionPredicateInput; +}; + + +export type MutationDeleteRowLevelPermissionPredicateGroupArgs = { + input: DeleteRowLevelPermissionPredicateGroupInput; +}; + + export type MutationDeleteSsoIdentityProviderArgs = { input: DeleteSsoInput; }; @@ -2710,6 +2765,16 @@ export type MutationUpdatePasswordViaResetTokenArgs = { }; +export type MutationUpdateRowLevelPermissionPredicateArgs = { + input: UpdateRowLevelPermissionPredicateInput; +}; + + +export type MutationUpdateRowLevelPermissionPredicateGroupArgs = { + input: UpdateRowLevelPermissionPredicateGroupInput; +}; + + export type MutationUpdateUserEmailArgs = { newEmail: Scalars['String']; verifyEmailRedirectPath?: InputMaybe; @@ -3221,6 +3286,9 @@ export type Query = { getQueueJobs: QueueJobsResponse; getQueueMetrics: QueueMetricsData; getRoles: Array; + getRowLevelPermissionPredicate?: Maybe; + getRowLevelPermissionPredicateGroups: Array; + getRowLevelPermissionPredicates: Array; getSSOIdentityProviders: Array; getServerlessFunctionSourceCode?: Maybe; getSystemHealthStatus: SystemHealth; @@ -3442,6 +3510,22 @@ export type QueryGetQueueMetricsArgs = { }; +export type QueryGetRowLevelPermissionPredicateArgs = { + id: Scalars['String']; +}; + + +export type QueryGetRowLevelPermissionPredicateGroupsArgs = { + roleId?: InputMaybe; +}; + + +export type QueryGetRowLevelPermissionPredicatesArgs = { + objectMetadataId?: InputMaybe; + roleId?: InputMaybe; +}; + + export type QueryGetServerlessFunctionSourceCodeArgs = { input: GetServerlessFunctionSourceCodeInput; }; @@ -3680,6 +3764,54 @@ export type RouteTriggerIdInput = { id: Scalars['String']; }; +export type RowLevelPermissionPredicate = { + __typename?: 'RowLevelPermissionPredicate'; + fieldMetadataId: Scalars['String']; + id: Scalars['String']; + objectMetadataId: Scalars['String']; + operand: RowLevelPermissionPredicateOperand; + positionInRowLevelPermissionPredicateGroup?: Maybe; + roleId: Scalars['String']; + rowLevelPermissionPredicateGroupId?: Maybe; + subFieldName?: Maybe; + value?: Maybe; + workspaceMemberFieldMetadataId?: Maybe; + workspaceMemberSubFieldName?: Maybe; +}; + +export type RowLevelPermissionPredicateGroup = { + __typename?: 'RowLevelPermissionPredicateGroup'; + id: Scalars['String']; + logicalOperator: RowLevelPermissionPredicateGroupLogicalOperator; + parentRowLevelPermissionPredicateGroupId?: Maybe; + positionInRowLevelPermissionPredicateGroup?: Maybe; + roleId: Scalars['String']; +}; + +export enum RowLevelPermissionPredicateGroupLogicalOperator { + AND = 'AND', + OR = 'OR' +} + +export enum RowLevelPermissionPredicateOperand { + CONTAINS = 'CONTAINS', + DOES_NOT_CONTAIN = 'DOES_NOT_CONTAIN', + GREATER_THAN_OR_EQUAL = 'GREATER_THAN_OR_EQUAL', + IS = 'IS', + IS_AFTER = 'IS_AFTER', + IS_BEFORE = 'IS_BEFORE', + IS_EMPTY = 'IS_EMPTY', + IS_IN_FUTURE = 'IS_IN_FUTURE', + IS_IN_PAST = 'IS_IN_PAST', + IS_NOT = 'IS_NOT', + IS_NOT_EMPTY = 'IS_NOT_EMPTY', + IS_NOT_NULL = 'IS_NOT_NULL', + IS_RELATIVE = 'IS_RELATIVE', + IS_TODAY = 'IS_TODAY', + LESS_THAN_OR_EQUAL = 'LESS_THAN_OR_EQUAL', + VECTOR_SEARCH = 'VECTOR_SEARCH' +} + export type RunWorkflowVersionInput = { /** Execution result in JSON format */ payload?: InputMaybe; @@ -4266,6 +4398,24 @@ export type UpdateRouteTriggerInputUpdates = { path: Scalars['String']; }; +export type UpdateRowLevelPermissionPredicateGroupInput = { + id: Scalars['String']; + logicalOperator?: InputMaybe; + parentRowLevelPermissionPredicateGroupId?: InputMaybe; + positionInRowLevelPermissionPredicateGroup?: InputMaybe; +}; + +export type UpdateRowLevelPermissionPredicateInput = { + id: Scalars['String']; + operand?: InputMaybe; + positionInRowLevelPermissionPredicateGroup?: InputMaybe; + rowLevelPermissionPredicateGroupId?: InputMaybe; + subFieldName?: InputMaybe; + value?: InputMaybe; + workspaceMemberFieldMetadataId?: InputMaybe; + workspaceMemberSubFieldName?: InputMaybe; +}; + export type UpdateServerlessFunctionInput = { /** Id of the serverless function to update */ id: Scalars['UUID']; 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 0e305af4bf..3e39dabc5d 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 @@ -29,6 +29,8 @@ export const useMetadataErrorHandler = () => { pageLayout: t`page layout`, pageLayoutTab: t`page layout tab`, pageLayoutWidget: t`page layout widget`, + rowLevelPermissionPredicate: t`row level permission predicate`, + rowLevelPermissionPredicateGroup: t`row level permission predicate group`, } as const satisfies Record; const handleMetadataError = useCallback( diff --git a/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/object-form/components/SettingsRolePermissionsObjectLevelObjectForm.tsx b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/object-form/components/SettingsRolePermissionsObjectLevelObjectForm.tsx index 36bd051c69..d9172853be 100644 --- a/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/object-form/components/SettingsRolePermissionsObjectLevelObjectForm.tsx +++ b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/object-form/components/SettingsRolePermissionsObjectLevelObjectForm.tsx @@ -2,15 +2,20 @@ import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMeta import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer'; import { SettingsRolePermissionsObjectLevelObjectFieldPermissionTable } from '@/settings/roles/role-permissions/object-level-permissions/field-permissions/components/SettingsRolePermissionsObjectLevelObjectFieldPermissionTable'; import { SettingsRolePermissionsObjectLevelObjectFormObjectLevel } from '@/settings/roles/role-permissions/object-level-permissions/object-form/components/SettingsRolePermissionsObjectLevelObjectFormObjectLevel'; +import { SettingsRolePermissionsObjectLevelRecordLevelSection } from '@/settings/roles/role-permissions/object-level-permissions/record-level-permissions/components/SettingsRolePermissionsObjectLevelRecordLevelSection'; import { settingsDraftRoleFamilyState } from '@/settings/roles/states/settingsDraftRoleFamilyState'; import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer'; +import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap'; import { t } from '@lingui/core/macro'; import { useSearchParams } from 'react-router-dom'; import { useRecoilValue } from 'recoil'; import { SettingsPath } from 'twenty-shared/types'; import { getSettingsPath, isDefined } from 'twenty-shared/utils'; import { Button } from 'twenty-ui/input'; -import { useFindOneAgentQuery } from '~/generated-metadata/graphql'; +import { + FeatureFlagKey, + useFindOneAgentQuery, +} from '~/generated-metadata/graphql'; type SettingsRolePermissionsObjectLevelObjectFormProps = { roleId: string; @@ -37,6 +42,10 @@ export const SettingsRolePermissionsObjectLevelObjectForm = ({ objectId: objectMetadataId, }); + const featureFlagsMap = useFeatureFlagsMap(); + const isRowLevelPermissionPredicatesEnabled = + featureFlagsMap[FeatureFlagKey.IS_ROW_LEVEL_PERMISSION_PREDICATES_ENABLED]; + const objectMetadataItem = objectMetadata.objectMetadataItem; const objectLabelSingular = objectMetadataItem.labelSingular; @@ -113,6 +122,9 @@ export const SettingsRolePermissionsObjectLevelObjectForm = ({ objectMetadataItem={objectMetadataItem} roleId={roleId} /> + {isRowLevelPermissionPredicatesEnabled && ( + + )} ); diff --git a/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/record-level-permissions/components/SettingsRolePermissionsObjectLevelRecordLevelSection.tsx b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/record-level-permissions/components/SettingsRolePermissionsObjectLevelRecordLevelSection.tsx new file mode 100644 index 0000000000..e430c3f3e3 --- /dev/null +++ b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/record-level-permissions/components/SettingsRolePermissionsObjectLevelRecordLevelSection.tsx @@ -0,0 +1,21 @@ +import styled from '@emotion/styled'; +import { t } from '@lingui/core/macro'; +import { H2Title } from 'twenty-ui/display'; +import { Section } from 'twenty-ui/layout'; + +const StyledEmptyState = styled.div` + color: ${({ theme }) => theme.font.color.tertiary}; + padding-top: ${({ theme }) => theme.spacing(2)}; +`; + +export const SettingsRolePermissionsObjectLevelRecordLevelSection = () => { + return ( +
+ + {t`No record-level rules have been set yet.`} +
+ ); +}; diff --git a/packages/twenty-server/@types/express.d.ts b/packages/twenty-server/@types/express.d.ts index 22bb31c446..d363df1cbb 100644 --- a/packages/twenty-server/@types/express.d.ts +++ b/packages/twenty-server/@types/express.d.ts @@ -19,6 +19,7 @@ declare module 'express-serve-static-core' { workspaceId?: string; workspaceMetadataVersion?: number; workspaceMemberId?: string; + workspaceMember?: WorkspaceMemberWorkspaceEntity; userWorkspaceId?: string; authProvider?: AuthProviderEnum | null; impersonationContext?: AuthContext['impersonationContext']; diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/common/1765499361805-addRLS.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1765499361805-addRLS.ts new file mode 100644 index 0000000000..99201cb128 --- /dev/null +++ b/packages/twenty-server/src/database/typeorm/core/migrations/common/1765499361805-addRLS.ts @@ -0,0 +1,131 @@ +import { type MigrationInterface, type QueryRunner } from 'typeorm'; + +export class AddRLS1765499361805 implements MigrationInterface { + name = 'AddRLS1765499361805'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TYPE "core"."rowLevelPermissionPredicate_operand_enum" AS ENUM('IS', 'IS_NOT_NULL', 'IS_NOT', 'LESS_THAN_OR_EQUAL', 'GREATER_THAN_OR_EQUAL', 'IS_BEFORE', 'IS_AFTER', 'CONTAINS', 'DOES_NOT_CONTAIN', 'IS_EMPTY', 'IS_NOT_EMPTY', 'IS_RELATIVE', 'IS_IN_PAST', 'IS_IN_FUTURE', 'IS_TODAY', 'VECTOR_SEARCH')`, + ); + await queryRunner.query( + `CREATE TABLE "core"."rowLevelPermissionPredicate" ("universalIdentifier" uuid, "applicationId" uuid, "id" uuid NOT NULL DEFAULT uuid_generate_v4(), "fieldMetadataId" uuid NOT NULL, "objectMetadataId" uuid NOT NULL, "operand" "core"."rowLevelPermissionPredicate_operand_enum" NOT NULL DEFAULT 'CONTAINS', "value" jsonb, "subFieldName" text, "workspaceMemberFieldMetadataId" uuid, "workspaceMemberSubFieldName" text, "rowLevelPermissionPredicateGroupId" uuid, "positionInRowLevelPermissionPredicateGroup" double precision, "workspaceId" uuid NOT NULL, "roleId" uuid NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP WITH TIME ZONE, CONSTRAINT "PK_e7ac2b75856fc7f300b5feb0e39" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_e46f3e01227f1c8ee0c8041821" ON "core"."rowLevelPermissionPredicate" ("workspaceId", "universalIdentifier") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_RLPP_GROUP_ID" ON "core"."rowLevelPermissionPredicate" ("rowLevelPermissionPredicateGroupId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_RLPP_FIELD_METADATA_ID" ON "core"."rowLevelPermissionPredicate" ("fieldMetadataId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_RLPP_WORKSPACE_ID_ROLE_ID_OBJECT_METADATA_ID" ON "core"."rowLevelPermissionPredicate" ("workspaceId", "roleId", "objectMetadataId") `, + ); + await queryRunner.query( + `CREATE TYPE "core"."rowLevelPermissionPredicateGroup_logicaloperator_enum" AS ENUM('AND', 'OR')`, + ); + await queryRunner.query( + `CREATE TABLE "core"."rowLevelPermissionPredicateGroup" ("universalIdentifier" uuid, "applicationId" uuid, "id" uuid NOT NULL DEFAULT uuid_generate_v4(), "parentRowLevelPermissionPredicateGroupId" uuid, "logicalOperator" "core"."rowLevelPermissionPredicateGroup_logicaloperator_enum" NOT NULL DEFAULT 'AND', "positionInRowLevelPermissionPredicateGroup" double precision, "workspaceId" uuid NOT NULL, "roleId" uuid NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP WITH TIME ZONE, CONSTRAINT "PK_5084d63eb632c38d70b974841f3" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_a14b5665091e86d461fb585924" ON "core"."rowLevelPermissionPredicateGroup" ("workspaceId", "universalIdentifier") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_RLPPG_WORKSPACE_ID_ROLE_ID" ON "core"."rowLevelPermissionPredicateGroup" ("workspaceId", "roleId") `, + ); + await queryRunner.query( + `ALTER TABLE "core"."rowLevelPermissionPredicate" ADD CONSTRAINT "FK_23b36d07d363f81200654fa1334" FOREIGN KEY ("applicationId") REFERENCES "core"."application"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "core"."rowLevelPermissionPredicate" ADD CONSTRAINT "FK_eadcbbb92b6d58c6785a780b5b7" FOREIGN KEY ("fieldMetadataId") REFERENCES "core"."fieldMetadata"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "core"."rowLevelPermissionPredicate" ADD CONSTRAINT "FK_d5ee96a9a03761f2c328a29523c" FOREIGN KEY ("objectMetadataId") REFERENCES "core"."objectMetadata"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "core"."rowLevelPermissionPredicate" ADD CONSTRAINT "FK_b575e84d5ba7f183079c5c8c421" FOREIGN KEY ("workspaceMemberFieldMetadataId") REFERENCES "core"."fieldMetadata"("id") ON DELETE SET NULL ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "core"."rowLevelPermissionPredicate" ADD CONSTRAINT "FK_15199deab40d48dd1480a2faf85" FOREIGN KEY ("workspaceId") REFERENCES "core"."workspace"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "core"."rowLevelPermissionPredicate" ADD CONSTRAINT "FK_5fb4b0cebaf1b6418412bf65170" FOREIGN KEY ("roleId") REFERENCES "core"."role"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "core"."rowLevelPermissionPredicate" ADD CONSTRAINT "FK_67519abd77fc637444720192737" FOREIGN KEY ("rowLevelPermissionPredicateGroupId") REFERENCES "core"."rowLevelPermissionPredicateGroup"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "core"."rowLevelPermissionPredicateGroup" ADD CONSTRAINT "FK_1e82563accb67114f65a3993b86" FOREIGN KEY ("applicationId") REFERENCES "core"."application"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "core"."rowLevelPermissionPredicateGroup" ADD CONSTRAINT "FK_a41b6a06e3a7ded2204b0fc815d" FOREIGN KEY ("workspaceId") REFERENCES "core"."workspace"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "core"."rowLevelPermissionPredicateGroup" ADD CONSTRAINT "FK_3f1abc7557a4e9f4334d41b07d7" FOREIGN KEY ("roleId") REFERENCES "core"."role"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "core"."rowLevelPermissionPredicateGroup" ADD CONSTRAINT "FK_25bbd97a29478e18061cb58950f" FOREIGN KEY ("parentRowLevelPermissionPredicateGroupId") REFERENCES "core"."rowLevelPermissionPredicateGroup"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "core"."rowLevelPermissionPredicateGroup" DROP CONSTRAINT "FK_25bbd97a29478e18061cb58950f"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."rowLevelPermissionPredicateGroup" DROP CONSTRAINT "FK_3f1abc7557a4e9f4334d41b07d7"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."rowLevelPermissionPredicateGroup" DROP CONSTRAINT "FK_a41b6a06e3a7ded2204b0fc815d"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."rowLevelPermissionPredicateGroup" DROP CONSTRAINT "FK_1e82563accb67114f65a3993b86"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."rowLevelPermissionPredicate" DROP CONSTRAINT "FK_67519abd77fc637444720192737"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."rowLevelPermissionPredicate" DROP CONSTRAINT "FK_5fb4b0cebaf1b6418412bf65170"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."rowLevelPermissionPredicate" DROP CONSTRAINT "FK_15199deab40d48dd1480a2faf85"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."rowLevelPermissionPredicate" DROP CONSTRAINT "FK_b575e84d5ba7f183079c5c8c421"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."rowLevelPermissionPredicate" DROP CONSTRAINT "FK_d5ee96a9a03761f2c328a29523c"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."rowLevelPermissionPredicate" DROP CONSTRAINT "FK_eadcbbb92b6d58c6785a780b5b7"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."rowLevelPermissionPredicate" DROP CONSTRAINT "FK_23b36d07d363f81200654fa1334"`, + ); + await queryRunner.query( + `DROP INDEX "core"."IDX_RLPPG_WORKSPACE_ID_ROLE_ID"`, + ); + await queryRunner.query( + `DROP INDEX "core"."IDX_a14b5665091e86d461fb585924"`, + ); + await queryRunner.query( + `DROP TABLE "core"."rowLevelPermissionPredicateGroup"`, + ); + await queryRunner.query( + `DROP TYPE "core"."rowLevelPermissionPredicateGroup_logicaloperator_enum"`, + ); + await queryRunner.query( + `DROP INDEX "core"."IDX_RLPP_WORKSPACE_ID_ROLE_ID_OBJECT_METADATA_ID"`, + ); + await queryRunner.query(`DROP INDEX "core"."IDX_RLPP_FIELD_METADATA_ID"`); + await queryRunner.query(`DROP INDEX "core"."IDX_RLPP_GROUP_ID"`); + await queryRunner.query( + `DROP INDEX "core"."IDX_e46f3e01227f1c8ee0c8041821"`, + ); + await queryRunner.query(`DROP TABLE "core"."rowLevelPermissionPredicate"`); + await queryRunner.query( + `DROP TYPE "core"."rowLevelPermissionPredicate_operand_enum"`, + ); + } +} diff --git a/packages/twenty-server/src/engine/api/common/interfaces/workspace-auth-context.interface.ts b/packages/twenty-server/src/engine/api/common/interfaces/workspace-auth-context.interface.ts index 1d58c5e974..a83ee148dd 100644 --- a/packages/twenty-server/src/engine/api/common/interfaces/workspace-auth-context.interface.ts +++ b/packages/twenty-server/src/engine/api/common/interfaces/workspace-auth-context.interface.ts @@ -6,6 +6,7 @@ interface AuthContextWithDefinedWorkspaceProperties { workspace: NonNullable; workspaceMetadataVersion?: string; workspaceMemberId: AuthContext['workspaceMemberId']; + workspaceMember: AuthContext['workspaceMember']; userWorkspaceId: AuthContext['userWorkspaceId']; apiKey: AuthContext['apiKey']; } diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/utils/create-query-runner-context.util.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/utils/create-query-runner-context.util.ts index 5cca2656c9..cc490f4d64 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/utils/create-query-runner-context.util.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/utils/create-query-runner-context.util.ts @@ -19,6 +19,7 @@ export const createQueryRunnerContext = ({ userWorkspace, authProvider, impersonationContext, + workspaceMember, } = request; return { @@ -32,6 +33,7 @@ export const createQueryRunnerContext = ({ userWorkspace, authProvider, impersonationContext, + workspaceMember, }, }; }; diff --git a/packages/twenty-server/src/engine/core-modules/auth/strategies/jwt.auth.strategy.spec.ts b/packages/twenty-server/src/engine/core-modules/auth/strategies/jwt.auth.strategy.spec.ts index 8242ed5f40..9625aafcc6 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/strategies/jwt.auth.strategy.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/strategies/jwt.auth.strategy.spec.ts @@ -23,6 +23,8 @@ describe('JwtAuthStrategy', () => { let applicationRepository: any; let jwtWrapperService: any; let permissionsService: any; + let globalWorkspaceOrmManager: any; + let workspaceMemberRepository: any; const jwt = { sub: 'sub-default', @@ -57,6 +59,20 @@ describe('JwtAuthStrategy', () => { permissionsService = { userHasWorkspaceSettingPermission: jest.fn(), }; + + workspaceMemberRepository = { + findOne: jest.fn(), + }; + workspaceMemberRepository.findOne.mockResolvedValue({ + id: 'workspace-member-id', + }); + + globalWorkspaceOrmManager = { + executeInWorkspaceContext: jest.fn(async (_authContext, callback) => { + return await callback(); + }), + getRepository: jest.fn(async () => workspaceMemberRepository), + }; }); afterEach(() => { @@ -80,6 +96,7 @@ describe('JwtAuthStrategy', () => { userWorkspaceRepository, apiKeyRepository, permissionsService, + globalWorkspaceOrmManager, ); await expect(strategy.validate(payload as JwtPayload)).rejects.toThrow( @@ -111,6 +128,7 @@ describe('JwtAuthStrategy', () => { userWorkspaceRepository, apiKeyRepository, permissionsService, + globalWorkspaceOrmManager, ); await expect(strategy.validate(payload as JwtPayload)).rejects.toThrow( @@ -145,6 +163,7 @@ describe('JwtAuthStrategy', () => { userWorkspaceRepository, apiKeyRepository, permissionsService, + globalWorkspaceOrmManager, ); await expect(strategy.validate(payload as JwtPayload)).rejects.toThrow( @@ -179,6 +198,7 @@ describe('JwtAuthStrategy', () => { userWorkspaceRepository, apiKeyRepository, permissionsService, + globalWorkspaceOrmManager, ); const result = await strategy.validate(payload as JwtPayload); @@ -220,10 +240,11 @@ describe('JwtAuthStrategy', () => { userWorkspaceRepository, apiKeyRepository, permissionsService, + globalWorkspaceOrmManager, ); await expect(strategy.validate(payload as JwtPayload)).rejects.toThrow( - new AuthException('UserWorkspaceEntity not found', expect.any(String), { + new AuthException('User not found', expect.any(String), { userFriendlyMessage: msg`User does not have access to this workspace.`, }), ); @@ -231,7 +252,7 @@ describe('JwtAuthStrategy', () => { try { await strategy.validate(payload as JwtPayload); } catch (e) { - expect(e.code).toBe(AuthExceptionCode.USER_WORKSPACE_NOT_FOUND); + expect(e.code).toBe(AuthExceptionCode.USER_NOT_FOUND); } }); @@ -261,6 +282,7 @@ describe('JwtAuthStrategy', () => { userWorkspaceRepository, apiKeyRepository, permissionsService, + globalWorkspaceOrmManager, ); await expect(strategy.validate(payload as JwtPayload)).rejects.toThrow( @@ -306,6 +328,7 @@ describe('JwtAuthStrategy', () => { userWorkspaceRepository, apiKeyRepository, permissionsService, + globalWorkspaceOrmManager, ); const user = await strategy.validate(payload as JwtPayload); @@ -339,6 +362,7 @@ describe('JwtAuthStrategy', () => { userWorkspaceRepository, apiKeyRepository, permissionsService, + globalWorkspaceOrmManager, ); await expect(strategy.validate(payload as JwtPayload)).rejects.toThrow( @@ -392,6 +416,7 @@ describe('JwtAuthStrategy', () => { userWorkspaceRepository, apiKeyRepository, permissionsService, + globalWorkspaceOrmManager, ); await expect(strategy.validate(payload as JwtPayload)).rejects.toThrow( @@ -438,6 +463,7 @@ describe('JwtAuthStrategy', () => { userWorkspaceRepository, apiKeyRepository, permissionsService, + globalWorkspaceOrmManager, ); await expect(strategy.validate(payload as JwtPayload)).rejects.toThrow( @@ -486,6 +512,7 @@ describe('JwtAuthStrategy', () => { userWorkspaceRepository, apiKeyRepository, permissionsService, + globalWorkspaceOrmManager, ); await expect(strategy.validate(payload as JwtPayload)).rejects.toThrow( @@ -545,6 +572,7 @@ describe('JwtAuthStrategy', () => { userWorkspaceRepository, apiKeyRepository, permissionsService, + globalWorkspaceOrmManager, ); await expect(strategy.validate(payload as JwtPayload)).rejects.toThrow( @@ -599,6 +627,7 @@ describe('JwtAuthStrategy', () => { userWorkspaceRepository, apiKeyRepository, permissionsService, + globalWorkspaceOrmManager, ); await expect(strategy.validate(payload as JwtPayload)).rejects.toThrow( @@ -670,6 +699,7 @@ describe('JwtAuthStrategy', () => { userWorkspaceRepository, apiKeyRepository, permissionsService, + globalWorkspaceOrmManager, ); await expect(strategy.validate(payload as JwtPayload)).rejects.toThrow( @@ -740,6 +770,7 @@ describe('JwtAuthStrategy', () => { userWorkspaceRepository, apiKeyRepository, permissionsService, + globalWorkspaceOrmManager, ); await expect(strategy.validate(payload as JwtPayload)).rejects.toThrow( @@ -811,6 +842,7 @@ describe('JwtAuthStrategy', () => { userWorkspaceRepository, apiKeyRepository, permissionsService, + globalWorkspaceOrmManager, ); await expect(strategy.validate(payload as JwtPayload)).rejects.toThrow( @@ -875,6 +907,7 @@ describe('JwtAuthStrategy', () => { userWorkspaceRepository, apiKeyRepository, permissionsService, + globalWorkspaceOrmManager, ); const result = await strategy.validate(payload as JwtPayload); @@ -941,6 +974,7 @@ describe('JwtAuthStrategy', () => { userWorkspaceRepository, apiKeyRepository, permissionsService, + globalWorkspaceOrmManager, ); const result = await strategy.validate(payload as JwtPayload); diff --git a/packages/twenty-server/src/engine/core-modules/auth/strategies/jwt.auth.strategy.ts b/packages/twenty-server/src/engine/core-modules/auth/strategies/jwt.auth.strategy.ts index b2196489bb..b4ce737a08 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/strategies/jwt.auth.strategy.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/strategies/jwt.auth.strategy.ts @@ -4,11 +4,13 @@ import { InjectRepository } from '@nestjs/typeorm'; import { msg } from '@lingui/core/macro'; import { Strategy } from 'passport-jwt'; -import { assertIsDefinedOrThrow, isDefined } from 'twenty-shared/utils'; -import { Repository } from 'typeorm'; import { PermissionFlagType } from 'twenty-shared/constants'; +import { assertIsDefinedOrThrow, isDefined } from 'twenty-shared/utils'; +import { WorkspaceActivationStatus } from 'twenty-shared/workspace'; +import { Repository } from 'typeorm'; import { ApiKeyEntity } from 'src/engine/core-modules/api-key/api-key.entity'; +import { ApplicationEntity } from 'src/engine/core-modules/application/application.entity'; import { AuthException, AuthExceptionCode, @@ -29,7 +31,9 @@ import { UserEntity } from 'src/engine/core-modules/user/user.entity'; import { userValidator } from 'src/engine/core-modules/user/user.validate'; import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; import { PermissionsService } from 'src/engine/metadata-modules/permissions/permissions.service'; -import { ApplicationEntity } from 'src/engine/core-modules/application/application.entity'; +import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager'; +import { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util'; +import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; @Injectable() export class JwtAuthStrategy extends PassportStrategy(Strategy, 'jwt') { @@ -46,6 +50,7 @@ export class JwtAuthStrategy extends PassportStrategy(Strategy, 'jwt') { @InjectRepository(ApiKeyEntity) private readonly apiKeyRepository: Repository, private readonly permissionsService: PermissionsService, + private readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager, ) { const jwtFromRequestFunction = jwtWrapperService.extractJwtFromRequest(); // @ts-expect-error legacy noImplicitAny @@ -146,6 +151,13 @@ export class JwtAuthStrategy extends PassportStrategy(Strategy, 'jwt') { where: { id: userId }, }); + if (!isDefined(user)) { + throw new AuthException( + 'User not found', + AuthExceptionCode.USER_NOT_FOUND, + ); + } + if (!payload.userWorkspaceId) { throw new AuthException( 'UserWorkspaceEntity not found', @@ -179,7 +191,50 @@ export class JwtAuthStrategy extends PassportStrategy(Strategy, 'jwt') { workspaceMemberId: payload.workspaceMemberId, }; - return context; + if ( + workspace.activationStatus === + WorkspaceActivationStatus.PENDING_CREATION || + workspace.activationStatus === WorkspaceActivationStatus.ONGOING_CREATION + ) { + return context; + } + + const workspaceMember = + await this.globalWorkspaceOrmManager.executeInWorkspaceContext( + buildSystemAuthContext(workspace.id), + async () => { + const workspaceMemberRepository = + await this.globalWorkspaceOrmManager.getRepository( + workspace.id, + 'workspaceMember', + { shouldBypassPermissionChecks: true }, + ); + + const workspaceMember = await workspaceMemberRepository.findOne({ + where: { + userId: user.id, + }, + }); + + assertIsDefinedOrThrow( + workspaceMember, + new AuthException( + 'User is not a member of the workspace', + AuthExceptionCode.FORBIDDEN_EXCEPTION, + { + userFriendlyMessage: msg`User is not a member of the workspace.`, + }, + ), + ); + + return workspaceMember; + }, + ); + + return { + ...context, + workspaceMember, + }; } private async validateImpersonation(payload: AccessTokenJwtPayload) { diff --git a/packages/twenty-server/src/engine/core-modules/auth/types/auth-context.type.ts b/packages/twenty-server/src/engine/core-modules/auth/types/auth-context.type.ts index 3b1e72891c..ad78afee27 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/types/auth-context.type.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/types/auth-context.type.ts @@ -1,14 +1,16 @@ import { type ApiKeyEntity } from 'src/engine/core-modules/api-key/api-key.entity'; +import { type ApplicationEntity } from 'src/engine/core-modules/application/application.entity'; import { type UserWorkspaceEntity } from 'src/engine/core-modules/user-workspace/user-workspace.entity'; import { type UserEntity } from 'src/engine/core-modules/user/user.entity'; import { type AuthProviderEnum } from 'src/engine/core-modules/workspace/types/workspace.type'; import { type WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; -import { type ApplicationEntity } from 'src/engine/core-modules/application/application.entity'; +import { type WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; export type AuthContext = { user?: UserEntity | null | undefined; apiKey?: ApiKeyEntity | null | undefined; workspaceMemberId?: string; + workspaceMember?: WorkspaceMemberWorkspaceEntity; workspace?: WorkspaceEntity; application?: ApplicationEntity | null | undefined; userWorkspaceId?: string; diff --git a/packages/twenty-server/src/engine/core-modules/core-engine.module.ts b/packages/twenty-server/src/engine/core-modules/core-engine.module.ts index 77776176a3..cd7a2db17d 100644 --- a/packages/twenty-server/src/engine/core-modules/core-engine.module.ts +++ b/packages/twenty-server/src/engine/core-modules/core-engine.module.ts @@ -60,6 +60,7 @@ import { AiModelsModule } from 'src/engine/metadata-modules/ai/ai-models/ai-mode import { FlatPageLayoutTabModule } from 'src/engine/metadata-modules/flat-page-layout-tab/flat-page-layout-tab.module'; import { PageLayoutModule } from 'src/engine/metadata-modules/page-layout/page-layout.module'; import { RoleModule } from 'src/engine/metadata-modules/role/role.module'; +import { RowLevelPermissionModule } from 'src/engine/metadata-modules/row-level-permission-predicate/row-level-permission.module'; import { SubscriptionsModule } from 'src/engine/subscriptions/subscriptions.module'; import { TrashCleanupModule } from 'src/engine/trash-cleanup/trash-cleanup.module'; import { WorkspaceEventEmitterModule } from 'src/engine/workspace-event-emitter/workspace-event-emitter.module'; @@ -81,6 +82,7 @@ import { FileModule } from './file/file.module'; ClientConfigModule, FeatureFlagModule, FileModule, + RowLevelPermissionModule, OpenApiModule, ApplicationModule, ApplicationSyncModule, @@ -150,6 +152,7 @@ import { FileModule } from './file/file.module'; ImpersonationModule, TrashCleanupModule, DashboardModule, + RowLevelPermissionModule, ], exports: [ AuditModule, diff --git a/packages/twenty-server/src/engine/core-modules/feature-flag/enums/feature-flag-key.enum.ts b/packages/twenty-server/src/engine/core-modules/feature-flag/enums/feature-flag-key.enum.ts index 3eec88f9fa..da30a139a1 100644 --- a/packages/twenty-server/src/engine/core-modules/feature-flag/enums/feature-flag-key.enum.ts +++ b/packages/twenty-server/src/engine/core-modules/feature-flag/enums/feature-flag-key.enum.ts @@ -14,5 +14,6 @@ export enum FeatureFlagKey { IS_DASHBOARD_V2_ENABLED = 'IS_DASHBOARD_V2_ENABLED', IS_TIMELINE_ACTIVITY_MIGRATED = 'IS_TIMELINE_ACTIVITY_MIGRATED', IS_GLOBAL_WORKSPACE_DATASOURCE_ENABLED = 'IS_GLOBAL_WORKSPACE_DATASOURCE_ENABLED', + IS_ROW_LEVEL_PERMISSION_PREDICATES_ENABLED = 'IS_ROW_LEVEL_PERMISSION_PREDICATES_ENABLED', IS_WORKSPACE_CREATION_V2_ENABLED = 'IS_WORKSPACE_CREATION_V2_ENABLED', } 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 3891751123..8c06edf766 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 @@ -11,6 +11,8 @@ import { FLAT_PAGE_LAYOUT_WIDGET_EDITABLE_PROPERTIES } from 'src/engine/metadata import { FLAT_PAGE_LAYOUT_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-page-layout/constants/flat-page-layout-editable-properties.constant'; import { FLAT_ROLE_TARGET_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-role-target/constants/flat-role-target-editable-properties.constant'; import { FLAT_ROLE_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-role/constants/flat-role-editable-properties.constant'; +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_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'; @@ -132,6 +134,20 @@ export const ALL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY = { ], propertiesToStringify: [], }, + rowLevelPermissionPredicate: { + propertiesToCompare: [ + ...FLAT_ROW_LEVEL_PERMISSION_PREDICATE_EDITABLE_PROPERTIES, + 'deletedAt', + ], + propertiesToStringify: ['value'], + }, + rowLevelPermissionPredicateGroup: { + propertiesToCompare: [ + ...FLAT_ROW_LEVEL_PERMISSION_PREDICATE_GROUP_EDITABLE_PROPERTIES, + 'deletedAt', + ], + 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 af5956352d..18f4b947f6 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 @@ -122,4 +122,6 @@ export const ALL_METADATA_RELATED_METADATA_BY_FOREIGN_KEY = { flatEntityForeignKeyAggregator: 'tabIds', }, }, + rowLevelPermissionPredicate: {}, + rowLevelPermissionPredicateGroup: {}, } 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 7094e457de..7ec83768b9 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 @@ -126,6 +126,23 @@ export const ALL_METADATA_RELATION_PROPERTIES = { objectMetadata: true, application: true, }, + rowLevelPermissionPredicate: { + workspace: true, + role: true, + fieldMetadata: true, + workspaceMemberFieldMetadata: true, + objectMetadata: true, + rowLevelPermissionPredicateGroup: true, + application: true, + }, + rowLevelPermissionPredicateGroup: { + workspace: true, + role: true, + parentRowLevelPermissionPredicateGroup: true, + childRowLevelPermissionPredicateGroups: true, + rowLevelPermissionPredicates: 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 eb0d45831a..c990db7397 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 @@ -64,4 +64,13 @@ export const ALL_METADATA_REQUIRED_METADATA_FOR_VALIDATION = { pageLayoutWidget: { pageLayoutTab: true, }, + rowLevelPermissionPredicate: { + fieldMetadata: true, + objectMetadata: true, + role: true, + rowLevelPermissionPredicateGroup: true, + }, + rowLevelPermissionPredicateGroup: { + role: true, + }, } as const satisfies MetadataRequiredForValidation; 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 6a19174edb..d234a5feca 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 @@ -9,6 +9,8 @@ import { WorkspaceFlatObjectMetadataMapCacheService } from 'src/engine/metadata- import { WorkspaceFlatPageLayoutTabMapCacheService } from 'src/engine/metadata-modules/flat-page-layout-tab/services/workspace-flat-page-layout-tab-map-cache.service'; import { WorkspaceFlatPageLayoutWidgetMapCacheService } from 'src/engine/metadata-modules/flat-page-layout-widget/services/workspace-flat-page-layout-widget-map-cache.service'; import { WorkspaceFlatPageLayoutMapCacheService } from 'src/engine/metadata-modules/flat-page-layout/services/workspace-flat-page-layout-map-cache.service'; +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 { 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'; @@ -19,6 +21,8 @@ import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadat import { PageLayoutTabEntity } from 'src/engine/metadata-modules/page-layout-tab/entities/page-layout-tab.entity'; import { PageLayoutWidgetEntity } from 'src/engine/metadata-modules/page-layout-widget/entities/page-layout-widget.entity'; import { PageLayoutEntity } from 'src/engine/metadata-modules/page-layout/entities/page-layout.entity'; +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 { 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'; @@ -40,6 +44,8 @@ import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache PageLayoutEntity, PageLayoutTabEntity, PageLayoutWidgetEntity, + RowLevelPermissionPredicateEntity, + RowLevelPermissionPredicateGroupEntity, ]), ], providers: [ @@ -54,6 +60,8 @@ import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache WorkspaceFlatPageLayoutMapCacheService, WorkspaceFlatPageLayoutTabMapCacheService, WorkspaceFlatPageLayoutWidgetMapCacheService, + WorkspaceFlatRowLevelPermissionPredicateMapCacheService, + WorkspaceFlatRowLevelPermissionPredicateGroupMapCacheService, ], exports: [ WorkspaceManyOrAllFlatEntityMapsCacheService, @@ -67,6 +75,8 @@ import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache WorkspaceFlatPageLayoutMapCacheService, WorkspaceFlatPageLayoutTabMapCacheService, WorkspaceFlatPageLayoutWidgetMapCacheService, + WorkspaceFlatRowLevelPermissionPredicateMapCacheService, + WorkspaceFlatRowLevelPermissionPredicateGroupMapCacheService, ], }) export class WorkspaceManyOrAllFlatEntityMapsCacheModule {} 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 acbac0643a..6bb4d78ebb 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 @@ -8,9 +8,9 @@ import { type FlatAgent } from 'src/engine/metadata-modules/flat-agent/types/fla import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type'; import { type FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type'; import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type'; -import { type FlatPageLayout } from 'src/engine/metadata-modules/flat-page-layout/types/flat-page-layout.type'; import { type FlatPageLayoutTab } from 'src/engine/metadata-modules/flat-page-layout-tab/types/flat-page-layout-tab.type'; import { type FlatPageLayoutWidget } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget.type'; +import { type FlatPageLayout } from 'src/engine/metadata-modules/flat-page-layout/types/flat-page-layout.type'; 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'; @@ -19,13 +19,17 @@ import { type FlatViewGroup } from 'src/engine/metadata-modules/flat-view-group/ import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type'; import { type IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity'; import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; -import { type PageLayoutEntity } from 'src/engine/metadata-modules/page-layout/entities/page-layout.entity'; import { type PageLayoutTabEntity } from 'src/engine/metadata-modules/page-layout-tab/entities/page-layout-tab.entity'; import { type PageLayoutWidgetEntity } from 'src/engine/metadata-modules/page-layout-widget/entities/page-layout-widget.entity'; +import { type PageLayoutEntity } from 'src/engine/metadata-modules/page-layout/entities/page-layout.entity'; import { type RoleTargetEntity } from 'src/engine/metadata-modules/role-target/role-target.entity'; import { type RoleEntity } from 'src/engine/metadata-modules/role/role.entity'; import { type RouteTriggerEntity } from 'src/engine/metadata-modules/route-trigger/route-trigger.entity'; import { type FlatRouteTrigger } from 'src/engine/metadata-modules/route-trigger/types/flat-route-trigger.type'; +import { type RowLevelPermissionPredicateGroupEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate-group.entity'; +import { type RowLevelPermissionPredicateEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate.entity'; +import { type FlatRowLevelPermissionPredicateGroup } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-group.type'; +import { type FlatRowLevelPermissionPredicate } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate.type'; 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'; @@ -61,11 +65,6 @@ import { type DeleteObjectAction, type UpdateObjectAction, } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/object/types/workspace-migration-object-action-v2'; -import { - type CreatePageLayoutAction, - type DeletePageLayoutAction, - type UpdatePageLayoutAction, -} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/page-layout/types/workspace-migration-page-layout-action-v2.type'; import { type CreatePageLayoutTabAction, type DeletePageLayoutTabAction, @@ -76,6 +75,11 @@ import { type DeletePageLayoutWidgetAction, type UpdatePageLayoutWidgetAction, } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/page-layout-widget/types/workspace-migration-page-layout-widget-action-v2.type'; +import { + type CreatePageLayoutAction, + type DeletePageLayoutAction, + type UpdatePageLayoutAction, +} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/page-layout/types/workspace-migration-page-layout-action-v2.type'; import { type CreateRoleTargetAction, type DeleteRoleTargetAction, @@ -91,6 +95,17 @@ import { type DeleteRouteTriggerAction, type UpdateRouteTriggerAction, } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/route-trigger/types/workspace-migration-route-trigger-action-v2.type'; +import { + type CreateRowLevelPermissionPredicateGroupAction, + type DeleteRowLevelPermissionPredicateGroupAction, + type UpdateRowLevelPermissionPredicateGroupAction, +} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/row-level-permission-predicate-group/types/workspace-migration-row-level-permission-predicate-group-action-v2.type'; +import { + type CreateRowLevelPermissionPredicateAction, + type DeleteRowLevelPermissionPredicateAction, + type DestroyRowLevelPermissionPredicateAction, + type UpdateRowLevelPermissionPredicateAction, +} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/row-level-permission-predicate/types/workspace-migration-row-level-permission-predicate-action-v2.type'; import { type CreateServerlessFunctionAction, type DeleteServerlessFunctionAction, @@ -163,6 +178,26 @@ export type AllFlatEntityTypesByMetadataName = { flatEntity: FlatViewGroup; entity: ViewGroupEntity; }; + rowLevelPermissionPredicate: { + actions: { + created: CreateRowLevelPermissionPredicateAction; + updated: UpdateRowLevelPermissionPredicateAction; + deleted: + | DeleteRowLevelPermissionPredicateAction + | DestroyRowLevelPermissionPredicateAction; + }; + flatEntity: FlatRowLevelPermissionPredicate; + entity: RowLevelPermissionPredicateEntity; + }; + rowLevelPermissionPredicateGroup: { + actions: { + created: CreateRowLevelPermissionPredicateGroupAction; + updated: UpdateRowLevelPermissionPredicateGroupAction; + deleted: DeleteRowLevelPermissionPredicateGroupAction; + }; + flatEntity: FlatRowLevelPermissionPredicateGroup; + entity: RowLevelPermissionPredicateGroupEntity; + }; index: { actions: { created: CreateIndexAction; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate-group/constants/flat-row-level-permission-predicate-group-editable-properties.constant.ts b/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate-group/constants/flat-row-level-permission-predicate-group-editable-properties.constant.ts new file mode 100644 index 0000000000..314bf0c58f --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate-group/constants/flat-row-level-permission-predicate-group-editable-properties.constant.ts @@ -0,0 +1,9 @@ +/* @license Enterprise */ + +import { type FlatRowLevelPermissionPredicateGroup } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-group.type'; + +export const FLAT_ROW_LEVEL_PERMISSION_PREDICATE_GROUP_EDITABLE_PROPERTIES = [ + 'logicalOperator', + 'positionInRowLevelPermissionPredicateGroup', + 'parentRowLevelPermissionPredicateGroupId', +] as const satisfies (keyof FlatRowLevelPermissionPredicateGroup)[]; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate-group/utils/from-create-row-level-permission-predicate-group-input-to-flat-row-level-permission-predicate-group-to-create.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate-group/utils/from-create-row-level-permission-predicate-group-input-to-flat-row-level-permission-predicate-group-to-create.util.ts new file mode 100644 index 0000000000..f4f4d8a540 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate-group/utils/from-create-row-level-permission-predicate-group-input-to-flat-row-level-permission-predicate-group-to-create.util.ts @@ -0,0 +1,55 @@ +/* @license Enterprise */ + +import { trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties } from 'twenty-shared/utils'; +import { v4 } from 'uuid'; + +import { type CreateRowLevelPermissionPredicateGroupInput } from 'src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/create-row-level-permission-predicate-group.input'; +import { type FlatRowLevelPermissionPredicateGroup } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-group.type'; + +export const fromCreateRowLevelPermissionPredicateGroupInputToFlatRowLevelPermissionPredicateGroupToCreate = + ({ + createRowLevelPermissionPredicateGroupInput: + rawCreateRowLevelPermissionPredicateGroupInput, + workspaceId, + }: { + createRowLevelPermissionPredicateGroupInput: CreateRowLevelPermissionPredicateGroupInput; + workspaceId: string; + }): FlatRowLevelPermissionPredicateGroup => { + const sanitizedInput = ( + trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties as unknown as ( + input: CreateRowLevelPermissionPredicateGroupInput, + keys: string[], + ) => CreateRowLevelPermissionPredicateGroupInput + )(rawCreateRowLevelPermissionPredicateGroupInput, [ + 'roleId', + 'parentRowLevelPermissionPredicateGroupId', + 'logicalOperator', + ]); + + const { roleId, ...createRowLevelPermissionPredicateGroupInput } = + sanitizedInput; + + const createdAt = new Date().toISOString(); + const predicateGroupId = v4(); + + return { + id: predicateGroupId, + workspaceId, + roleId, + childRowLevelPermissionPredicateGroupIds: [], + rowLevelPermissionPredicateIds: [], + createdAt, + updatedAt: createdAt, + deletedAt: null, + universalIdentifier: predicateGroupId, + parentRowLevelPermissionPredicateGroupId: + createRowLevelPermissionPredicateGroupInput.parentRowLevelPermissionPredicateGroupId ?? + null, + logicalOperator: + createRowLevelPermissionPredicateGroupInput.logicalOperator, + positionInRowLevelPermissionPredicateGroup: + createRowLevelPermissionPredicateGroupInput.positionInRowLevelPermissionPredicateGroup ?? + null, + applicationId: null, + }; + }; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate-group/utils/from-delete-row-level-permission-predicate-group-input-to-flat-row-level-permission-predicate-group-or-throw.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate-group/utils/from-delete-row-level-permission-predicate-group-input-to-flat-row-level-permission-predicate-group-or-throw.util.ts new file mode 100644 index 0000000000..d2c66d2c3e --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate-group/utils/from-delete-row-level-permission-predicate-group-input-to-flat-row-level-permission-predicate-group-or-throw.util.ts @@ -0,0 +1,45 @@ +/* @license Enterprise */ + +import { t } from '@lingui/core/macro'; +import { + extractAndSanitizeObjectStringFields, + isDefined, +} from 'twenty-shared/utils'; + +import { type DeleteRowLevelPermissionPredicateGroupInput } from 'src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/delete-row-level-permission-predicate-group.input'; +import { + RowLevelPermissionPredicateGroupException, + RowLevelPermissionPredicateGroupExceptionCode, +} from 'src/engine/metadata-modules/row-level-permission-predicate/exceptions/row-level-permission-predicate-group.exception'; +import { type FlatRowLevelPermissionPredicateGroupMaps } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-group-maps.type'; +import { type FlatRowLevelPermissionPredicateGroup } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-group.type'; + +export const fromDeleteRowLevelPermissionPredicateGroupInputToFlatRowLevelPermissionPredicateGroupOrThrow = + ({ + deleteRowLevelPermissionPredicateGroupInput: + rawDeleteRowLevelPermissionPredicateGroupInput, + flatRowLevelPermissionPredicateGroupMaps, + }: { + deleteRowLevelPermissionPredicateGroupInput: DeleteRowLevelPermissionPredicateGroupInput; + flatRowLevelPermissionPredicateGroupMaps: FlatRowLevelPermissionPredicateGroupMaps; + }): FlatRowLevelPermissionPredicateGroup => { + const { id: predicateGroupId } = extractAndSanitizeObjectStringFields( + rawDeleteRowLevelPermissionPredicateGroupInput, + ['id'], + ); + + const existingFlatPredicateGroup = + flatRowLevelPermissionPredicateGroupMaps.byId[predicateGroupId]; + + if (!isDefined(existingFlatPredicateGroup)) { + throw new RowLevelPermissionPredicateGroupException( + t`Row level permission predicate group to delete not found`, + RowLevelPermissionPredicateGroupExceptionCode.ROW_LEVEL_PERMISSION_PREDICATE_GROUP_NOT_FOUND, + ); + } + + return { + ...existingFlatPredicateGroup, + deletedAt: new Date().toISOString(), + }; + }; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate-group/utils/from-destroy-row-level-permission-predicate-group-input-to-flat-row-level-permission-predicate-group-or-throw.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate-group/utils/from-destroy-row-level-permission-predicate-group-input-to-flat-row-level-permission-predicate-group-or-throw.util.ts new file mode 100644 index 0000000000..6d0f09a32a --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate-group/utils/from-destroy-row-level-permission-predicate-group-input-to-flat-row-level-permission-predicate-group-or-throw.util.ts @@ -0,0 +1,42 @@ +/* @license Enterprise */ + +import { t } from '@lingui/core/macro'; +import { + extractAndSanitizeObjectStringFields, + isDefined, +} from 'twenty-shared/utils'; + +import { type DestroyRowLevelPermissionPredicateGroupInput } from 'src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/destroy-row-level-permission-predicate-group.input'; +import { + RowLevelPermissionPredicateGroupException, + RowLevelPermissionPredicateGroupExceptionCode, +} from 'src/engine/metadata-modules/row-level-permission-predicate/exceptions/row-level-permission-predicate-group.exception'; +import { type FlatRowLevelPermissionPredicateGroupMaps } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-group-maps.type'; +import { type FlatRowLevelPermissionPredicateGroup } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-group.type'; + +export const fromDestroyRowLevelPermissionPredicateGroupInputToFlatRowLevelPermissionPredicateGroupOrThrow = + ({ + destroyRowLevelPermissionPredicateGroupInput: + rawDestroyRowLevelPermissionPredicateGroupInput, + flatRowLevelPermissionPredicateGroupMaps, + }: { + destroyRowLevelPermissionPredicateGroupInput: DestroyRowLevelPermissionPredicateGroupInput; + flatRowLevelPermissionPredicateGroupMaps: FlatRowLevelPermissionPredicateGroupMaps; + }): FlatRowLevelPermissionPredicateGroup => { + const { id: predicateGroupId } = extractAndSanitizeObjectStringFields( + rawDestroyRowLevelPermissionPredicateGroupInput, + ['id'], + ); + + const existingFlatPredicateGroup = + flatRowLevelPermissionPredicateGroupMaps.byId[predicateGroupId]; + + if (!isDefined(existingFlatPredicateGroup)) { + throw new RowLevelPermissionPredicateGroupException( + t`Row level permission predicate group to destroy not found`, + RowLevelPermissionPredicateGroupExceptionCode.ROW_LEVEL_PERMISSION_PREDICATE_GROUP_NOT_FOUND, + ); + } + + return existingFlatPredicateGroup; + }; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate-group/utils/from-flat-row-level-permission-predicate-group-to-dto.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate-group/utils/from-flat-row-level-permission-predicate-group-to-dto.util.ts new file mode 100644 index 0000000000..07f65ba224 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate-group/utils/from-flat-row-level-permission-predicate-group-to-dto.util.ts @@ -0,0 +1,10 @@ +/* @license Enterprise */ + +import { type RowLevelPermissionPredicateGroupDTO } from 'src/engine/metadata-modules/row-level-permission-predicate/dtos/row-level-permission-predicate-group.dto'; +import { type FlatRowLevelPermissionPredicateGroup } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-group.type'; + +export const fromFlatRowLevelPermissionPredicateGroupToDto = ( + flatGroup: FlatRowLevelPermissionPredicateGroup, +): RowLevelPermissionPredicateGroupDTO => ({ + ...flatGroup, +}); diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate-group/utils/from-update-row-level-permission-predicate-group-input-to-flat-row-level-permission-predicate-group-to-update-or-throw.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate-group/utils/from-update-row-level-permission-predicate-group-input-to-flat-row-level-permission-predicate-group-to-update-or-throw.util.ts new file mode 100644 index 0000000000..0ce279fecd --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate-group/utils/from-update-row-level-permission-predicate-group-input-to-flat-row-level-permission-predicate-group-to-update-or-throw.util.ts @@ -0,0 +1,55 @@ +/* @license Enterprise */ + +import { t } from '@lingui/core/macro'; +import { + extractAndSanitizeObjectStringFields, + isDefined, + trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties, +} from 'twenty-shared/utils'; + +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 { type UpdateRowLevelPermissionPredicateGroupInput } from 'src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/update-row-level-permission-predicate-group.input'; +import { + RowLevelPermissionPredicateGroupException, + RowLevelPermissionPredicateGroupExceptionCode, +} from 'src/engine/metadata-modules/row-level-permission-predicate/exceptions/row-level-permission-predicate-group.exception'; +import { type FlatRowLevelPermissionPredicateGroupMaps } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-group-maps.type'; +import { type FlatRowLevelPermissionPredicateGroup } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-group.type'; +import { mergeUpdateInExistingRecord } from 'src/utils/merge-update-in-existing-record.util'; + +export const fromUpdateRowLevelPermissionPredicateGroupInputToFlatRowLevelPermissionPredicateGroupToUpdateOrThrow = + ({ + updateRowLevelPermissionPredicateGroupInput: + rawUpdateRowLevelPermissionPredicateGroupInput, + flatRowLevelPermissionPredicateGroupMaps, + }: { + updateRowLevelPermissionPredicateGroupInput: UpdateRowLevelPermissionPredicateGroupInput; + flatRowLevelPermissionPredicateGroupMaps: FlatRowLevelPermissionPredicateGroupMaps; + }): FlatRowLevelPermissionPredicateGroup => { + const { id: predicateGroupId } = + trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties( + rawUpdateRowLevelPermissionPredicateGroupInput, + ['id'], + ); + + const existingFlatPredicateGroup = + flatRowLevelPermissionPredicateGroupMaps.byId[predicateGroupId]; + + if (!isDefined(existingFlatPredicateGroup)) { + throw new RowLevelPermissionPredicateGroupException( + t`Row level permission predicate group to update not found`, + RowLevelPermissionPredicateGroupExceptionCode.ROW_LEVEL_PERMISSION_PREDICATE_GROUP_NOT_FOUND, + ); + } + + const sanitizedUpdate = extractAndSanitizeObjectStringFields( + rawUpdateRowLevelPermissionPredicateGroupInput, + FLAT_ROW_LEVEL_PERMISSION_PREDICATE_GROUP_EDITABLE_PROPERTIES, + ); + + return mergeUpdateInExistingRecord({ + existing: existingFlatPredicateGroup, + properties: FLAT_ROW_LEVEL_PERMISSION_PREDICATE_GROUP_EDITABLE_PROPERTIES, + update: sanitizedUpdate, + }); + }; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/constants/flat-row-level-permission-predicate-editable-properties.constant.ts b/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/constants/flat-row-level-permission-predicate-editable-properties.constant.ts new file mode 100644 index 0000000000..5972588aaf --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/constants/flat-row-level-permission-predicate-editable-properties.constant.ts @@ -0,0 +1,13 @@ +/* @license Enterprise */ + +import { type FlatRowLevelPermissionPredicate } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate.type'; + +export const FLAT_ROW_LEVEL_PERMISSION_PREDICATE_EDITABLE_PROPERTIES = [ + 'operand', + 'value', + 'rowLevelPermissionPredicateGroupId', + 'positionInRowLevelPermissionPredicateGroup', + 'subFieldName', + 'workspaceMemberFieldMetadataId', + 'workspaceMemberSubFieldName', +] as const satisfies (keyof FlatRowLevelPermissionPredicate)[]; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/constants/row-level-permission-predicate-entity-relation-properties.constant.ts b/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/constants/row-level-permission-predicate-entity-relation-properties.constant.ts new file mode 100644 index 0000000000..c206c39bf1 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/constants/row-level-permission-predicate-entity-relation-properties.constant.ts @@ -0,0 +1,11 @@ +/* @license Enterprise */ + +export const ROW_LEVEL_PERMISSION_PREDICATE_ENTITY_RELATION_PROPERTIES = [ + 'workspace', + 'role', + 'fieldMetadata', + 'workspaceMemberFieldMetadata', + 'objectMetadata', + 'rowLevelPermissionPredicateGroup', + 'application', +] as const; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/constants/row-level-permission-predicate-group-entity-relation-properties.constant.ts b/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/constants/row-level-permission-predicate-group-entity-relation-properties.constant.ts new file mode 100644 index 0000000000..2c6a838a74 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/constants/row-level-permission-predicate-group-entity-relation-properties.constant.ts @@ -0,0 +1,10 @@ +/* @license Enterprise */ + +export const ROW_LEVEL_PERMISSION_PREDICATE_GROUP_ENTITY_RELATION_PROPERTIES = [ + 'workspace', + 'role', + 'parentRowLevelPermissionPredicateGroup', + 'childRowLevelPermissionPredicateGroups', + 'rowLevelPermissionPredicates', + 'application', +] as const; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/services/workspace-flat-row-level-permission-predicate-group-map-cache.service.ts b/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/services/workspace-flat-row-level-permission-predicate-group-map-cache.service.ts new file mode 100644 index 0000000000..3636f9e3fd --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/services/workspace-flat-row-level-permission-predicate-group-map-cache.service.ts @@ -0,0 +1,55 @@ +/* @license Enterprise */ + +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; + +import { 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 { fromRowLevelPermissionPredicateGroupEntityToFlatRowLevelPermissionPredicateGroup } from 'src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-row-level-permission-predicate-group-entity-to-flat-row-level-permission-predicate-group.util'; +import { RowLevelPermissionPredicateGroupEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate-group.entity'; +import { type FlatRowLevelPermissionPredicateGroupMaps } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-group-maps.type'; +import { WorkspaceCache } from 'src/engine/workspace-cache/decorators/workspace-cache.decorator'; +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('flatRowLevelPermissionPredicateGroupMaps') +export class WorkspaceFlatRowLevelPermissionPredicateGroupMapCacheService extends WorkspaceCacheProvider { + constructor( + @InjectRepository(RowLevelPermissionPredicateGroupEntity) + private readonly rowLevelPermissionPredicateGroupRepository: Repository, + ) { + super(); + } + + async computeForCache( + workspaceId: string, + ): Promise { + const rowLevelPermissionPredicateGroups = + await this.rowLevelPermissionPredicateGroupRepository.find({ + where: { + workspaceId, + }, + withDeleted: true, + }); + + const flatRowLevelPermissionPredicateGroupMaps = + createEmptyFlatEntityMaps(); + + for (const rowLevelPermissionPredicateGroupEntity of rowLevelPermissionPredicateGroups) { + const flatRowLevelPermissionPredicateGroup = + fromRowLevelPermissionPredicateGroupEntityToFlatRowLevelPermissionPredicateGroup( + rowLevelPermissionPredicateGroupEntity, + ); + + addFlatEntityToFlatEntityMapsThroughMutationOrThrow({ + flatEntity: flatRowLevelPermissionPredicateGroup, + flatEntityMapsToMutate: flatRowLevelPermissionPredicateGroupMaps, + }); + } + + return flatRowLevelPermissionPredicateGroupMaps; + } +} diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/services/workspace-flat-row-level-permission-predicate-map-cache.service.ts b/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/services/workspace-flat-row-level-permission-predicate-map-cache.service.ts new file mode 100644 index 0000000000..a66e0392ff --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/services/workspace-flat-row-level-permission-predicate-map-cache.service.ts @@ -0,0 +1,54 @@ +/* @license Enterprise */ + +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; + +import { 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 { fromRowLevelPermissionPredicateEntityToFlatRowLevelPermissionPredicate } from 'src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-row-level-permission-predicate-entity-to-flat-row-level-permission-predicate.util'; +import { RowLevelPermissionPredicateEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate.entity'; +import { type FlatRowLevelPermissionPredicateMaps } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-maps.type'; +import { WorkspaceCache } from 'src/engine/workspace-cache/decorators/workspace-cache.decorator'; +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('flatRowLevelPermissionPredicateMaps') +export class WorkspaceFlatRowLevelPermissionPredicateMapCacheService extends WorkspaceCacheProvider { + constructor( + @InjectRepository(RowLevelPermissionPredicateEntity) + private readonly rowLevelPermissionPredicateRepository: Repository, + ) { + super(); + } + + async computeForCache( + workspaceId: string, + ): Promise { + const rowLevelPermissionPredicates = + await this.rowLevelPermissionPredicateRepository.find({ + where: { + workspaceId, + }, + withDeleted: true, + }); + + const flatRowLevelPermissionPredicateMaps = createEmptyFlatEntityMaps(); + + for (const rowLevelPermissionPredicateEntity of rowLevelPermissionPredicates) { + const flatRowLevelPermissionPredicate = + fromRowLevelPermissionPredicateEntityToFlatRowLevelPermissionPredicate( + rowLevelPermissionPredicateEntity, + ); + + addFlatEntityToFlatEntityMapsThroughMutationOrThrow({ + flatEntity: flatRowLevelPermissionPredicate, + flatEntityMapsToMutate: flatRowLevelPermissionPredicateMaps, + }); + } + + return flatRowLevelPermissionPredicateMaps; + } +} diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-create-row-level-permission-predicate-input-to-flat-row-level-permission-predicate-to-create.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-create-row-level-permission-predicate-input-to-flat-row-level-permission-predicate-to-create.util.ts new file mode 100644 index 0000000000..91ea3d22bf --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-create-row-level-permission-predicate-input-to-flat-row-level-permission-predicate-to-create.util.ts @@ -0,0 +1,72 @@ +/* @license Enterprise */ + +import { trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties } from 'twenty-shared/utils'; +import { v4 } from 'uuid'; + +import { type CreateRowLevelPermissionPredicateInput } from 'src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/create-row-level-permission-predicate.input'; +import { type FlatRowLevelPermissionPredicate } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate.type'; + +export const fromCreateRowLevelPermissionPredicateInputToFlatRowLevelPermissionPredicateToCreate = + ({ + createRowLevelPermissionPredicateInput: + rawCreateRowLevelPermissionPredicateInput, + workspaceId, + }: { + createRowLevelPermissionPredicateInput: CreateRowLevelPermissionPredicateInput; + workspaceId: string; + }): FlatRowLevelPermissionPredicate => { + const sanitizedInput = ( + trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties as unknown as ( + input: CreateRowLevelPermissionPredicateInput, + keys: string[], + ) => CreateRowLevelPermissionPredicateInput + )(rawCreateRowLevelPermissionPredicateInput, [ + 'fieldMetadataId', + 'objectMetadataId', + 'roleId', + 'rowLevelPermissionPredicateGroupId', + 'workspaceMemberFieldMetadataId', + 'subFieldName', + 'workspaceMemberSubFieldName', + 'operand', + ]); + + const { + fieldMetadataId, + objectMetadataId, + roleId, + value, + ...createRowLevelPermissionPredicateInput + } = sanitizedInput; + + const createdAt = new Date().toISOString(); + const predicateId = v4(); + + return { + id: predicateId, + fieldMetadataId, + objectMetadataId, + roleId, + workspaceId, + createdAt, + updatedAt: createdAt, + deletedAt: null, + universalIdentifier: predicateId, + operand: createRowLevelPermissionPredicateInput.operand, + value: value, + rowLevelPermissionPredicateGroupId: + createRowLevelPermissionPredicateInput.rowLevelPermissionPredicateGroupId ?? + null, + positionInRowLevelPermissionPredicateGroup: + createRowLevelPermissionPredicateInput.positionInRowLevelPermissionPredicateGroup ?? + null, + subFieldName: createRowLevelPermissionPredicateInput.subFieldName ?? null, + workspaceMemberFieldMetadataId: + createRowLevelPermissionPredicateInput.workspaceMemberFieldMetadataId ?? + null, + workspaceMemberSubFieldName: + createRowLevelPermissionPredicateInput.workspaceMemberSubFieldName ?? + null, + applicationId: null, + }; + }; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-delete-row-level-permission-predicate-input-to-flat-row-level-permission-predicate-or-throw.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-delete-row-level-permission-predicate-input-to-flat-row-level-permission-predicate-or-throw.util.ts new file mode 100644 index 0000000000..0e742492ca --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-delete-row-level-permission-predicate-input-to-flat-row-level-permission-predicate-or-throw.util.ts @@ -0,0 +1,45 @@ +/* @license Enterprise */ + +import { t } from '@lingui/core/macro'; +import { + extractAndSanitizeObjectStringFields, + isDefined, +} from 'twenty-shared/utils'; + +import { type DeleteRowLevelPermissionPredicateInput } from 'src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/delete-row-level-permission-predicate.input'; +import { + RowLevelPermissionPredicateException, + RowLevelPermissionPredicateExceptionCode, +} from 'src/engine/metadata-modules/row-level-permission-predicate/exceptions/row-level-permission-predicate.exception'; +import { type FlatRowLevelPermissionPredicateMaps } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-maps.type'; +import { type FlatRowLevelPermissionPredicate } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate.type'; + +export const fromDeleteRowLevelPermissionPredicateInputToFlatRowLevelPermissionPredicateOrThrow = + ({ + deleteRowLevelPermissionPredicateInput: + rawDeleteRowLevelPermissionPredicateInput, + flatRowLevelPermissionPredicateMaps, + }: { + deleteRowLevelPermissionPredicateInput: DeleteRowLevelPermissionPredicateInput; + flatRowLevelPermissionPredicateMaps: FlatRowLevelPermissionPredicateMaps; + }): FlatRowLevelPermissionPredicate => { + const { id: predicateId } = extractAndSanitizeObjectStringFields( + rawDeleteRowLevelPermissionPredicateInput, + ['id'], + ); + + const existingFlatPredicate = + flatRowLevelPermissionPredicateMaps.byId[predicateId]; + + if (!isDefined(existingFlatPredicate)) { + throw new RowLevelPermissionPredicateException( + t`Row level permission predicate to delete not found`, + RowLevelPermissionPredicateExceptionCode.ROW_LEVEL_PERMISSION_PREDICATE_NOT_FOUND, + ); + } + + return { + ...existingFlatPredicate, + deletedAt: new Date().toISOString(), + }; + }; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-destroy-row-level-permission-predicate-input-to-flat-row-level-permission-predicate-or-throw.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-destroy-row-level-permission-predicate-input-to-flat-row-level-permission-predicate-or-throw.util.ts new file mode 100644 index 0000000000..07ec70f58a --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-destroy-row-level-permission-predicate-input-to-flat-row-level-permission-predicate-or-throw.util.ts @@ -0,0 +1,42 @@ +/* @license Enterprise */ + +import { t } from '@lingui/core/macro'; +import { + extractAndSanitizeObjectStringFields, + isDefined, +} from 'twenty-shared/utils'; + +import { type DestroyRowLevelPermissionPredicateInput } from 'src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/destroy-row-level-permission-predicate.input'; +import { + RowLevelPermissionPredicateException, + RowLevelPermissionPredicateExceptionCode, +} from 'src/engine/metadata-modules/row-level-permission-predicate/exceptions/row-level-permission-predicate.exception'; +import { type FlatRowLevelPermissionPredicateMaps } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-maps.type'; +import { type FlatRowLevelPermissionPredicate } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate.type'; + +export const fromDestroyRowLevelPermissionPredicateInputToFlatRowLevelPermissionPredicateOrThrow = + ({ + destroyRowLevelPermissionPredicateInput: + rawDestroyRowLevelPermissionPredicateInput, + flatRowLevelPermissionPredicateMaps, + }: { + destroyRowLevelPermissionPredicateInput: DestroyRowLevelPermissionPredicateInput; + flatRowLevelPermissionPredicateMaps: FlatRowLevelPermissionPredicateMaps; + }): FlatRowLevelPermissionPredicate => { + const { id: predicateId } = extractAndSanitizeObjectStringFields( + rawDestroyRowLevelPermissionPredicateInput, + ['id'], + ); + + const existingFlatPredicate = + flatRowLevelPermissionPredicateMaps.byId[predicateId]; + + if (!isDefined(existingFlatPredicate)) { + throw new RowLevelPermissionPredicateException( + t`Row level permission predicate to destroy not found`, + RowLevelPermissionPredicateExceptionCode.ROW_LEVEL_PERMISSION_PREDICATE_NOT_FOUND, + ); + } + + return existingFlatPredicate; + }; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-flat-row-level-permission-predicate-to-dto.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-flat-row-level-permission-predicate-to-dto.util.ts new file mode 100644 index 0000000000..f9400d0743 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-flat-row-level-permission-predicate-to-dto.util.ts @@ -0,0 +1,10 @@ +/* @license Enterprise */ + +import { type RowLevelPermissionPredicateDTO } from 'src/engine/metadata-modules/row-level-permission-predicate/dtos/row-level-permission-predicate.dto'; +import { type FlatRowLevelPermissionPredicate } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate.type'; + +export const fromFlatRowLevelPermissionPredicateToDto = ( + flatPredicate: FlatRowLevelPermissionPredicate, +): RowLevelPermissionPredicateDTO => ({ + ...flatPredicate, +}); diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-row-level-permission-predicate-entity-to-flat-row-level-permission-predicate.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-row-level-permission-predicate-entity-to-flat-row-level-permission-predicate.util.ts new file mode 100644 index 0000000000..4391e1a704 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-row-level-permission-predicate-entity-to-flat-row-level-permission-predicate.util.ts @@ -0,0 +1,28 @@ +/* @license Enterprise */ + +import { removePropertiesFromRecord } from 'twenty-shared/utils'; + +import { ROW_LEVEL_PERMISSION_PREDICATE_ENTITY_RELATION_PROPERTIES } from 'src/engine/metadata-modules/flat-row-level-permission-predicate/constants/row-level-permission-predicate-entity-relation-properties.constant'; +import { type RowLevelPermissionPredicateEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate.entity'; +import { type FlatRowLevelPermissionPredicate } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate.type'; + +export const fromRowLevelPermissionPredicateEntityToFlatRowLevelPermissionPredicate = + ( + rowLevelPermissionPredicateEntity: RowLevelPermissionPredicateEntity, + ): FlatRowLevelPermissionPredicate => { + const rowLevelPermissionPredicateEntityWithoutRelations = + removePropertiesFromRecord(rowLevelPermissionPredicateEntity, [ + ...ROW_LEVEL_PERMISSION_PREDICATE_ENTITY_RELATION_PROPERTIES, + ] as (typeof ROW_LEVEL_PERMISSION_PREDICATE_ENTITY_RELATION_PROPERTIES)[number][]); + + return { + ...rowLevelPermissionPredicateEntityWithoutRelations, + createdAt: rowLevelPermissionPredicateEntity.createdAt.toISOString(), + updatedAt: rowLevelPermissionPredicateEntity.updatedAt.toISOString(), + deletedAt: + rowLevelPermissionPredicateEntity.deletedAt?.toISOString() ?? null, + universalIdentifier: + rowLevelPermissionPredicateEntityWithoutRelations.universalIdentifier ?? + rowLevelPermissionPredicateEntityWithoutRelations.id, + }; + }; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-row-level-permission-predicate-group-entity-to-flat-row-level-permission-predicate-group.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-row-level-permission-predicate-group-entity-to-flat-row-level-permission-predicate-group.util.ts new file mode 100644 index 0000000000..e80c4103b5 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-row-level-permission-predicate-group-entity-to-flat-row-level-permission-predicate-group.util.ts @@ -0,0 +1,36 @@ +/* @license Enterprise */ + +import { removePropertiesFromRecord } from 'twenty-shared/utils'; + +import { ROW_LEVEL_PERMISSION_PREDICATE_GROUP_ENTITY_RELATION_PROPERTIES } from 'src/engine/metadata-modules/flat-row-level-permission-predicate/constants/row-level-permission-predicate-group-entity-relation-properties.constant'; +import { type RowLevelPermissionPredicateGroupEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate-group.entity'; +import { type FlatRowLevelPermissionPredicateGroup } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-group.type'; + +export const fromRowLevelPermissionPredicateGroupEntityToFlatRowLevelPermissionPredicateGroup = + ( + rowLevelPermissionPredicateGroupEntity: RowLevelPermissionPredicateGroupEntity, + ): FlatRowLevelPermissionPredicateGroup => { + const rowLevelPermissionPredicateGroupEntityWithoutRelations = + removePropertiesFromRecord(rowLevelPermissionPredicateGroupEntity, [ + ...ROW_LEVEL_PERMISSION_PREDICATE_GROUP_ENTITY_RELATION_PROPERTIES, + ] as (typeof ROW_LEVEL_PERMISSION_PREDICATE_GROUP_ENTITY_RELATION_PROPERTIES)[number][]); + + return { + ...rowLevelPermissionPredicateGroupEntityWithoutRelations, + createdAt: rowLevelPermissionPredicateGroupEntity.createdAt.toISOString(), + updatedAt: rowLevelPermissionPredicateGroupEntity.updatedAt.toISOString(), + deletedAt: + rowLevelPermissionPredicateGroupEntity.deletedAt?.toISOString() ?? null, + universalIdentifier: + rowLevelPermissionPredicateGroupEntityWithoutRelations.universalIdentifier ?? + rowLevelPermissionPredicateGroupEntityWithoutRelations.id, + childRowLevelPermissionPredicateGroupIds: + rowLevelPermissionPredicateGroupEntity.childRowLevelPermissionPredicateGroups.map( + ({ id }) => id, + ), + rowLevelPermissionPredicateIds: + rowLevelPermissionPredicateGroupEntity.rowLevelPermissionPredicates.map( + ({ id }) => id, + ), + }; + }; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-update-row-level-permission-predicate-input-to-flat-row-level-permission-predicate-to-update-or-throw.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-update-row-level-permission-predicate-input-to-flat-row-level-permission-predicate-to-update-or-throw.util.ts new file mode 100644 index 0000000000..a9ef2ff505 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-update-row-level-permission-predicate-input-to-flat-row-level-permission-predicate-to-update-or-throw.util.ts @@ -0,0 +1,55 @@ +/* @license Enterprise */ + +import { t } from '@lingui/core/macro'; +import { + extractAndSanitizeObjectStringFields, + isDefined, + trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties, +} from 'twenty-shared/utils'; + +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 { type UpdateRowLevelPermissionPredicateInput } from 'src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/update-row-level-permission-predicate.input'; +import { + RowLevelPermissionPredicateException, + RowLevelPermissionPredicateExceptionCode, +} from 'src/engine/metadata-modules/row-level-permission-predicate/exceptions/row-level-permission-predicate.exception'; +import { type FlatRowLevelPermissionPredicateMaps } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-maps.type'; +import { type FlatRowLevelPermissionPredicate } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate.type'; +import { mergeUpdateInExistingRecord } from 'src/utils/merge-update-in-existing-record.util'; + +export const fromUpdateRowLevelPermissionPredicateInputToFlatRowLevelPermissionPredicateToUpdateOrThrow = + ({ + updateRowLevelPermissionPredicateInput: + rawUpdateRowLevelPermissionPredicateInput, + flatRowLevelPermissionPredicateMaps, + }: { + updateRowLevelPermissionPredicateInput: UpdateRowLevelPermissionPredicateInput; + flatRowLevelPermissionPredicateMaps: FlatRowLevelPermissionPredicateMaps; + }): FlatRowLevelPermissionPredicate => { + const { id: predicateId } = + trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties( + rawUpdateRowLevelPermissionPredicateInput, + ['id'], + ); + + const existingFlatPredicate = + flatRowLevelPermissionPredicateMaps.byId[predicateId]; + + if (!isDefined(existingFlatPredicate)) { + throw new RowLevelPermissionPredicateException( + t`Row level permission predicate to update not found`, + RowLevelPermissionPredicateExceptionCode.ROW_LEVEL_PERMISSION_PREDICATE_NOT_FOUND, + ); + } + + const sanitizedUpdate = extractAndSanitizeObjectStringFields( + rawUpdateRowLevelPermissionPredicateInput, + FLAT_ROW_LEVEL_PERMISSION_PREDICATE_EDITABLE_PROPERTIES, + ); + + return mergeUpdateInExistingRecord({ + existing: existingFlatPredicate, + properties: FLAT_ROW_LEVEL_PERMISSION_PREDICATE_EDITABLE_PROPERTIES, + update: sanitizedUpdate, + }); + }; diff --git a/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/create-row-level-permission-predicate-group.input.ts b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/create-row-level-permission-predicate-group.input.ts new file mode 100644 index 0000000000..1474f614b9 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/create-row-level-permission-predicate-group.input.ts @@ -0,0 +1,20 @@ +/* @license Enterprise */ + +import { Field, InputType } from '@nestjs/graphql'; + +import { RowLevelPermissionPredicateGroupLogicalOperator } from 'src/engine/metadata-modules/row-level-permission-predicate/enums/row-level-permission-predicate-group-logical-operator.enum'; + +@InputType() +export class CreateRowLevelPermissionPredicateGroupInput { + @Field(() => String, { nullable: true }) + parentRowLevelPermissionPredicateGroupId?: string | null; + + @Field(() => RowLevelPermissionPredicateGroupLogicalOperator) + logicalOperator: RowLevelPermissionPredicateGroupLogicalOperator; + + @Field(() => Number, { nullable: true }) + positionInRowLevelPermissionPredicateGroup?: number | null; + + @Field(() => String) + roleId: string; +} diff --git a/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/create-row-level-permission-predicate.input.ts b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/create-row-level-permission-predicate.input.ts new file mode 100644 index 0000000000..eca0c282cf --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/create-row-level-permission-predicate.input.ts @@ -0,0 +1,41 @@ +/* @license Enterprise */ + +import { Field, InputType } from '@nestjs/graphql'; + +import GraphQLJSON from 'graphql-type-json'; + +import { RowLevelPermissionPredicateOperand } from 'src/engine/metadata-modules/row-level-permission-predicate/enums/row-level-permission-predicate-operand'; +import { RowLevelPermissionPredicateValue } from 'src/engine/metadata-modules/row-level-permission-predicate/types/row-level-permission-predicate-value.type'; + +@InputType() +export class CreateRowLevelPermissionPredicateInput { + @Field(() => String) + fieldMetadataId: string; + + @Field(() => String) + objectMetadataId: string; + + @Field(() => RowLevelPermissionPredicateOperand) + operand: RowLevelPermissionPredicateOperand; + + @Field(() => GraphQLJSON, { nullable: true }) + value: RowLevelPermissionPredicateValue | null; + + @Field(() => String, { nullable: true }) + subFieldName?: string | null; + + @Field(() => String, { nullable: true }) + workspaceMemberFieldMetadataId?: string | null; + + @Field(() => String, { nullable: true }) + workspaceMemberSubFieldName?: string | null; + + @Field(() => String, { nullable: true }) + rowLevelPermissionPredicateGroupId?: string | null; + + @Field(() => Number, { nullable: true }) + positionInRowLevelPermissionPredicateGroup?: number | null; + + @Field(() => String) + roleId: string; +} diff --git a/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/delete-row-level-permission-predicate-group.input.ts b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/delete-row-level-permission-predicate-group.input.ts new file mode 100644 index 0000000000..5bf5714005 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/delete-row-level-permission-predicate-group.input.ts @@ -0,0 +1,9 @@ +/* @license Enterprise */ + +import { Field, InputType } from '@nestjs/graphql'; + +@InputType() +export class DeleteRowLevelPermissionPredicateGroupInput { + @Field(() => String) + id: string; +} diff --git a/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/delete-row-level-permission-predicate.input.ts b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/delete-row-level-permission-predicate.input.ts new file mode 100644 index 0000000000..fc3a71e7f7 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/delete-row-level-permission-predicate.input.ts @@ -0,0 +1,9 @@ +/* @license Enterprise */ + +import { Field, InputType } from '@nestjs/graphql'; + +@InputType() +export class DeleteRowLevelPermissionPredicateInput { + @Field(() => String) + id: string; +} diff --git a/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/destroy-row-level-permission-predicate-group.input.ts b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/destroy-row-level-permission-predicate-group.input.ts new file mode 100644 index 0000000000..14dc103d97 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/destroy-row-level-permission-predicate-group.input.ts @@ -0,0 +1,18 @@ +/* @license Enterprise */ + +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 DestroyRowLevelPermissionPredicateGroupInput { + @IDField(() => UUIDScalarType, { + description: + 'The id of the row level permission predicate group to destroy.', + }) + @IsUUID() + id: string; +} diff --git a/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/destroy-row-level-permission-predicate.input.ts b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/destroy-row-level-permission-predicate.input.ts new file mode 100644 index 0000000000..bbd36ff993 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/destroy-row-level-permission-predicate.input.ts @@ -0,0 +1,17 @@ +/* @license Enterprise */ + +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 DestroyRowLevelPermissionPredicateInput { + @IDField(() => UUIDScalarType, { + description: 'The id of the row level permission predicate to destroy.', + }) + @IsUUID() + id: string; +} diff --git a/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/update-row-level-permission-predicate-group.input.ts b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/update-row-level-permission-predicate-group.input.ts new file mode 100644 index 0000000000..a71608cf0e --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/update-row-level-permission-predicate-group.input.ts @@ -0,0 +1,22 @@ +/* @license Enterprise */ + +import { Field, InputType } from '@nestjs/graphql'; + +import { RowLevelPermissionPredicateGroupLogicalOperator } from 'src/engine/metadata-modules/row-level-permission-predicate/enums/row-level-permission-predicate-group-logical-operator.enum'; + +@InputType() +export class UpdateRowLevelPermissionPredicateGroupInput { + @Field(() => String) + id: string; + + @Field(() => String, { nullable: true }) + parentRowLevelPermissionPredicateGroupId?: string | null; + + @Field(() => RowLevelPermissionPredicateGroupLogicalOperator, { + nullable: true, + }) + logicalOperator?: RowLevelPermissionPredicateGroupLogicalOperator; + + @Field(() => Number, { nullable: true }) + positionInRowLevelPermissionPredicateGroup?: number | null; +} diff --git a/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/update-row-level-permission-predicate.input.ts b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/update-row-level-permission-predicate.input.ts new file mode 100644 index 0000000000..9a66977111 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/update-row-level-permission-predicate.input.ts @@ -0,0 +1,35 @@ +/* @license Enterprise */ + +import { Field, InputType } from '@nestjs/graphql'; + +import GraphQLJSON from 'graphql-type-json'; + +import { RowLevelPermissionPredicateOperand } from 'src/engine/metadata-modules/row-level-permission-predicate/enums/row-level-permission-predicate-operand'; +import { RowLevelPermissionPredicateValue } from 'src/engine/metadata-modules/row-level-permission-predicate/types/row-level-permission-predicate-value.type'; + +@InputType() +export class UpdateRowLevelPermissionPredicateInput { + @Field(() => String) + id: string; + + @Field(() => RowLevelPermissionPredicateOperand, { nullable: true }) + operand?: RowLevelPermissionPredicateOperand; + + @Field(() => GraphQLJSON, { nullable: true }) + value?: RowLevelPermissionPredicateValue; + + @Field(() => String, { nullable: true }) + subFieldName?: string | null; + + @Field(() => String, { nullable: true }) + workspaceMemberFieldMetadataId?: string | null; + + @Field(() => String, { nullable: true }) + workspaceMemberSubFieldName?: string | null; + + @Field(() => String, { nullable: true }) + rowLevelPermissionPredicateGroupId?: string | null; + + @Field(() => Number, { nullable: true }) + positionInRowLevelPermissionPredicateGroup?: number | null; +} diff --git a/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/row-level-permission-predicate-group.dto.ts b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/row-level-permission-predicate-group.dto.ts new file mode 100644 index 0000000000..18aae03b85 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/row-level-permission-predicate-group.dto.ts @@ -0,0 +1,26 @@ +/* @license Enterprise */ + +import { Field, ObjectType, registerEnumType } from '@nestjs/graphql'; + +import { RowLevelPermissionPredicateGroupLogicalOperator } from 'src/engine/metadata-modules/row-level-permission-predicate/enums/row-level-permission-predicate-group-logical-operator.enum'; + +registerEnumType(RowLevelPermissionPredicateGroupLogicalOperator, { + name: 'RowLevelPermissionPredicateGroupLogicalOperator', +}); +@ObjectType('RowLevelPermissionPredicateGroup') +export class RowLevelPermissionPredicateGroupDTO { + @Field(() => String) + id: string; + + @Field(() => String, { nullable: true }) + parentRowLevelPermissionPredicateGroupId?: string | null; + + @Field(() => RowLevelPermissionPredicateGroupLogicalOperator) + logicalOperator: RowLevelPermissionPredicateGroupLogicalOperator; + + @Field(() => Number, { nullable: true }) + positionInRowLevelPermissionPredicateGroup?: number | null; + + @Field(() => String) + roleId: string; +} diff --git a/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/row-level-permission-predicate.dto.ts b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/row-level-permission-predicate.dto.ts new file mode 100644 index 0000000000..9ebdc5c8b9 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/row-level-permission-predicate.dto.ts @@ -0,0 +1,47 @@ +/* @license Enterprise */ + +import { Field, ObjectType, registerEnumType } from '@nestjs/graphql'; + +import GraphQLJSON from 'graphql-type-json'; + +import { RowLevelPermissionPredicateOperand } from 'src/engine/metadata-modules/row-level-permission-predicate/enums/row-level-permission-predicate-operand'; +import { RowLevelPermissionPredicateValue } from 'src/engine/metadata-modules/row-level-permission-predicate/types/row-level-permission-predicate-value.type'; + +registerEnumType(RowLevelPermissionPredicateOperand, { + name: 'RowLevelPermissionPredicateOperand', +}); +@ObjectType('RowLevelPermissionPredicate') +export class RowLevelPermissionPredicateDTO { + @Field(() => String) + id: string; + + @Field(() => String) + fieldMetadataId: string; + + @Field(() => String) + objectMetadataId: string; + + @Field(() => RowLevelPermissionPredicateOperand) + operand: RowLevelPermissionPredicateOperand; + + @Field(() => String, { nullable: true }) + subFieldName?: string | null; + + @Field(() => String, { nullable: true }) + workspaceMemberFieldMetadataId?: string | null; + + @Field(() => String, { nullable: true }) + workspaceMemberSubFieldName?: string | null; + + @Field(() => String, { nullable: true }) + rowLevelPermissionPredicateGroupId?: string | null; + + @Field(() => Number, { nullable: true }) + positionInRowLevelPermissionPredicateGroup?: number | null; + + @Field(() => String) + roleId: string; + + @Field(() => GraphQLJSON, { nullable: true }) + value: RowLevelPermissionPredicateValue | null; +} diff --git a/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate-group.entity.ts b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate-group.entity.ts new file mode 100644 index 0000000000..a3323ecae8 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate-group.entity.ts @@ -0,0 +1,82 @@ +/* @license Enterprise */ + +import { + Column, + CreateDateColumn, + DeleteDateColumn, + Entity, + Index, + JoinColumn, + ManyToOne, + OneToMany, + PrimaryGeneratedColumn, + Relation, + UpdateDateColumn, +} from 'typeorm'; + +import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity'; +import { RowLevelPermissionPredicateEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate.entity'; +import { RowLevelPermissionPredicateGroupLogicalOperator } from 'src/engine/metadata-modules/row-level-permission-predicate/enums/row-level-permission-predicate-group-logical-operator.enum'; +import { SyncableEntity } from 'src/engine/workspace-manager/workspace-sync/types/syncable-entity.interface'; + +@Entity({ name: 'rowLevelPermissionPredicateGroup', schema: 'core' }) +@Index('IDX_RLPPG_WORKSPACE_ID_ROLE_ID', ['workspaceId', 'roleId']) +export class RowLevelPermissionPredicateGroupEntity + extends SyncableEntity + implements Required +{ + @PrimaryGeneratedColumn('uuid') + id: string; + + @Column({ nullable: true, type: 'uuid' }) + parentRowLevelPermissionPredicateGroupId: string | null; + + @Column({ + type: 'enum', + enum: Object.values(RowLevelPermissionPredicateGroupLogicalOperator), + nullable: false, + default: RowLevelPermissionPredicateGroupLogicalOperator.AND, + }) + logicalOperator: RowLevelPermissionPredicateGroupLogicalOperator; + + @Column({ nullable: true, type: 'double precision' }) + positionInRowLevelPermissionPredicateGroup: number | null; + + @Column({ nullable: false, type: 'uuid' }) + roleId: string; + + @CreateDateColumn({ type: 'timestamptz' }) + createdAt: Date; + + @UpdateDateColumn({ type: 'timestamptz' }) + updatedAt: Date; + + @DeleteDateColumn({ type: 'timestamptz' }) + deletedAt: Date | null; + + @ManyToOne(() => RoleEntity, { onDelete: 'CASCADE' }) + @JoinColumn({ name: 'roleId' }) + role: Relation; + + @ManyToOne( + () => RowLevelPermissionPredicateGroupEntity, + (rowLevelPermissionPredicateGroup) => + rowLevelPermissionPredicateGroup.childRowLevelPermissionPredicateGroups, + { onDelete: 'CASCADE' }, + ) + @JoinColumn({ name: 'parentRowLevelPermissionPredicateGroupId' }) + parentRowLevelPermissionPredicateGroup: Relation; + + @OneToMany( + () => RowLevelPermissionPredicateGroupEntity, + (rowLevelPermissionPredicateGroup) => + rowLevelPermissionPredicateGroup.parentRowLevelPermissionPredicateGroup, + ) + childRowLevelPermissionPredicateGroups: Relation[]; + + @OneToMany( + () => RowLevelPermissionPredicateEntity, + (predicate) => predicate.rowLevelPermissionPredicateGroup, + ) + rowLevelPermissionPredicates: Relation[]; +} diff --git a/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate.entity.ts b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate.entity.ts new file mode 100644 index 0000000000..f7f20369af --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate.entity.ts @@ -0,0 +1,115 @@ +/* @license Enterprise */ + +import { + Column, + CreateDateColumn, + DeleteDateColumn, + Entity, + Index, + JoinColumn, + ManyToOne, + PrimaryGeneratedColumn, + Relation, + UpdateDateColumn, +} from 'typeorm'; + +import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; +import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; +import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity'; +import { RowLevelPermissionPredicateGroupEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate-group.entity'; +import { RowLevelPermissionPredicateOperand } from 'src/engine/metadata-modules/row-level-permission-predicate/enums/row-level-permission-predicate-operand'; +import { type RowLevelPermissionPredicateValue } from 'src/engine/metadata-modules/row-level-permission-predicate/types/row-level-permission-predicate-value.type'; +import { SyncableEntity } from 'src/engine/workspace-manager/workspace-sync/types/syncable-entity.interface'; + +@Entity({ name: 'rowLevelPermissionPredicate', schema: 'core' }) +@Index('IDX_RLPP_WORKSPACE_ID_ROLE_ID_OBJECT_METADATA_ID', [ + 'workspaceId', + 'roleId', + 'objectMetadataId', +]) +@Index('IDX_RLPP_FIELD_METADATA_ID', ['fieldMetadataId']) +@Index('IDX_RLPP_GROUP_ID', ['rowLevelPermissionPredicateGroupId']) +export class RowLevelPermissionPredicateEntity + extends SyncableEntity + implements Required +{ + @PrimaryGeneratedColumn('uuid') + id: string; + + @Column({ nullable: false, type: 'uuid' }) + fieldMetadataId: string; + + @ManyToOne(() => FieldMetadataEntity, { + onDelete: 'CASCADE', + }) + @JoinColumn({ name: 'fieldMetadataId' }) + fieldMetadata: Relation; + + @Column({ nullable: false, type: 'uuid' }) + objectMetadataId: string; + + @ManyToOne(() => ObjectMetadataEntity, { + onDelete: 'CASCADE', + }) + @JoinColumn({ name: 'objectMetadataId' }) + objectMetadata: Relation; + + @Column({ + nullable: false, + type: 'enum', + enum: Object.values(RowLevelPermissionPredicateOperand), + default: RowLevelPermissionPredicateOperand.CONTAINS, + }) + operand: RowLevelPermissionPredicateOperand; + + @Column({ nullable: true, type: 'jsonb' }) + value: RowLevelPermissionPredicateValue | null; + + @Column({ nullable: true, type: 'text', default: null }) + subFieldName: string | null; + + @Column({ nullable: true, type: 'uuid' }) + workspaceMemberFieldMetadataId: string | null; + + @Column({ nullable: true, type: 'text', default: null }) + workspaceMemberSubFieldName: string | null; + + @ManyToOne(() => FieldMetadataEntity, { + onDelete: 'SET NULL', + }) + @JoinColumn({ name: 'workspaceMemberFieldMetadataId' }) + workspaceMemberFieldMetadata: Relation; + + @Column({ nullable: true, type: 'uuid' }) + rowLevelPermissionPredicateGroupId: string | null; + + @Column({ nullable: true, type: 'double precision' }) + positionInRowLevelPermissionPredicateGroup: number | null; + + @Column({ nullable: false, type: 'uuid' }) + roleId: string; + + @CreateDateColumn({ type: 'timestamptz' }) + createdAt: Date; + + @UpdateDateColumn({ type: 'timestamptz' }) + updatedAt: Date; + + @DeleteDateColumn({ type: 'timestamptz' }) + deletedAt: Date | null; + + @ManyToOne(() => RoleEntity, { onDelete: 'CASCADE' }) + @JoinColumn({ name: 'roleId' }) + role: Relation; + + @ManyToOne( + () => RowLevelPermissionPredicateGroupEntity, + (rowLevelPermissionPredicateGroup) => + rowLevelPermissionPredicateGroup.rowLevelPermissionPredicates, + { + onDelete: 'CASCADE', + }, + ) + @JoinColumn({ name: 'rowLevelPermissionPredicateGroupId' }) + rowLevelPermissionPredicateGroup: Relation; +} diff --git a/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/enums/row-level-permission-predicate-group-logical-operator.enum.ts b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/enums/row-level-permission-predicate-group-logical-operator.enum.ts new file mode 100644 index 0000000000..35bcc4ed9b --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/enums/row-level-permission-predicate-group-logical-operator.enum.ts @@ -0,0 +1,6 @@ +/* @license Enterprise */ + +export enum RowLevelPermissionPredicateGroupLogicalOperator { + AND = 'AND', + OR = 'OR', +} diff --git a/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/enums/row-level-permission-predicate-operand.ts b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/enums/row-level-permission-predicate-operand.ts new file mode 100644 index 0000000000..fc1c10aff4 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/enums/row-level-permission-predicate-operand.ts @@ -0,0 +1,20 @@ +/* @license Enterprise */ + +export enum RowLevelPermissionPredicateOperand { + IS = 'IS', + IS_NOT_NULL = 'IS_NOT_NULL', + IS_NOT = 'IS_NOT', + LESS_THAN_OR_EQUAL = 'LESS_THAN_OR_EQUAL', + GREATER_THAN_OR_EQUAL = 'GREATER_THAN_OR_EQUAL', + IS_BEFORE = 'IS_BEFORE', + IS_AFTER = 'IS_AFTER', + CONTAINS = 'CONTAINS', + DOES_NOT_CONTAIN = 'DOES_NOT_CONTAIN', + IS_EMPTY = 'IS_EMPTY', + IS_NOT_EMPTY = 'IS_NOT_EMPTY', + IS_RELATIVE = 'IS_RELATIVE', + IS_IN_PAST = 'IS_IN_PAST', + IS_IN_FUTURE = 'IS_IN_FUTURE', + IS_TODAY = 'IS_TODAY', + VECTOR_SEARCH = 'VECTOR_SEARCH', +} diff --git a/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/exceptions/row-level-permission-predicate-group.exception.ts b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/exceptions/row-level-permission-predicate-group.exception.ts new file mode 100644 index 0000000000..5c1d090884 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/exceptions/row-level-permission-predicate-group.exception.ts @@ -0,0 +1,44 @@ +/* @license Enterprise */ + +import { type MessageDescriptor } from '@lingui/core'; +import { msg } from '@lingui/core/macro'; + +import { + appendCommonExceptionCode, + CustomException, +} from 'src/utils/custom-exception'; + +export const RowLevelPermissionPredicateGroupExceptionCode = + appendCommonExceptionCode({ + ROW_LEVEL_PERMISSION_PREDICATE_GROUP_NOT_FOUND: + 'ROW_LEVEL_PERMISSION_PREDICATE_GROUP_NOT_FOUND', + INVALID_ROW_LEVEL_PERMISSION_PREDICATE_GROUP_DATA: + 'INVALID_ROW_LEVEL_PERMISSION_PREDICATE_GROUP_DATA', + ROLE_NOT_FOUND: 'ROLE_NOT_FOUND', + } as const); + +const rowLevelPermissionPredicateGroupExceptionUserFriendlyMessages: Record< + keyof typeof RowLevelPermissionPredicateGroupExceptionCode, + MessageDescriptor +> = { + ROW_LEVEL_PERMISSION_PREDICATE_GROUP_NOT_FOUND: msg`Row level permission predicate group not found.`, + INVALID_ROW_LEVEL_PERMISSION_PREDICATE_GROUP_DATA: msg`Invalid row level permission predicate group data.`, + ROLE_NOT_FOUND: msg`Role not found.`, + INTERNAL_SERVER_ERROR: msg`An unexpected error occurred.`, +}; + +export class RowLevelPermissionPredicateGroupException extends CustomException< + keyof typeof RowLevelPermissionPredicateGroupExceptionCode +> { + constructor( + message: string, + code: keyof typeof RowLevelPermissionPredicateGroupExceptionCode, + { userFriendlyMessage }: { userFriendlyMessage?: MessageDescriptor } = {}, + ) { + super(message, code, { + userFriendlyMessage: + userFriendlyMessage ?? + rowLevelPermissionPredicateGroupExceptionUserFriendlyMessages[code], + }); + } +} diff --git a/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/exceptions/row-level-permission-predicate.exception.ts b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/exceptions/row-level-permission-predicate.exception.ts new file mode 100644 index 0000000000..9f8c1ba83d --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/exceptions/row-level-permission-predicate.exception.ts @@ -0,0 +1,48 @@ +/* @license Enterprise */ + +import { type MessageDescriptor } from '@lingui/core'; +import { msg } from '@lingui/core/macro'; + +import { + appendCommonExceptionCode, + CustomException, +} from 'src/utils/custom-exception'; + +export const RowLevelPermissionPredicateExceptionCode = + appendCommonExceptionCode({ + ROW_LEVEL_PERMISSION_PREDICATE_NOT_FOUND: + 'ROW_LEVEL_PERMISSION_PREDICATE_NOT_FOUND', + INVALID_ROW_LEVEL_PERMISSION_PREDICATE_DATA: + 'INVALID_ROW_LEVEL_PERMISSION_PREDICATE_DATA', + FIELD_METADATA_NOT_FOUND: 'FIELD_METADATA_NOT_FOUND', + OBJECT_METADATA_NOT_FOUND: 'OBJECT_METADATA_NOT_FOUND', + ROLE_NOT_FOUND: 'ROLE_NOT_FOUND', + } as const); + +const rowLevelPermissionPredicateExceptionUserFriendlyMessages: Record< + keyof typeof RowLevelPermissionPredicateExceptionCode, + MessageDescriptor +> = { + ROW_LEVEL_PERMISSION_PREDICATE_NOT_FOUND: msg`Row level permission predicate not found.`, + INVALID_ROW_LEVEL_PERMISSION_PREDICATE_DATA: msg`Invalid row level permission predicate data.`, + FIELD_METADATA_NOT_FOUND: msg`Field metadata not found.`, + OBJECT_METADATA_NOT_FOUND: msg`Object metadata not found.`, + ROLE_NOT_FOUND: msg`Role not found.`, + INTERNAL_SERVER_ERROR: msg`An unexpected error occurred.`, +}; + +export class RowLevelPermissionPredicateException extends CustomException< + keyof typeof RowLevelPermissionPredicateExceptionCode +> { + constructor( + message: string, + code: keyof typeof RowLevelPermissionPredicateExceptionCode, + { userFriendlyMessage }: { userFriendlyMessage?: MessageDescriptor } = {}, + ) { + super(message, code, { + userFriendlyMessage: + userFriendlyMessage ?? + rowLevelPermissionPredicateExceptionUserFriendlyMessages[code], + }); + } +} diff --git a/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/resolvers/row-level-permission-predicate-group.resolver.ts b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/resolvers/row-level-permission-predicate-group.resolver.ts new file mode 100644 index 0000000000..3267cc5da9 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/resolvers/row-level-permission-predicate-group.resolver.ts @@ -0,0 +1,81 @@ +/* @license Enterprise */ + +import { UseGuards } from '@nestjs/common'; +import { Args, Mutation, Query, Resolver } from '@nestjs/graphql'; + +import { 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 { CreateRowLevelPermissionPredicateGroupInput } from 'src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/create-row-level-permission-predicate-group.input'; +import { DeleteRowLevelPermissionPredicateGroupInput } from 'src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/delete-row-level-permission-predicate-group.input'; +import { UpdateRowLevelPermissionPredicateGroupInput } from 'src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/update-row-level-permission-predicate-group.input'; +import { RowLevelPermissionPredicateGroupDTO } from 'src/engine/metadata-modules/row-level-permission-predicate/dtos/row-level-permission-predicate-group.dto'; +import { RowLevelPermissionPredicateGroupLogicalOperator } from 'src/engine/metadata-modules/row-level-permission-predicate/enums/row-level-permission-predicate-group-logical-operator.enum'; +import { RowLevelPermissionPredicateGroupService } from 'src/engine/metadata-modules/row-level-permission-predicate/services/row-level-permission-predicate-group.service'; + +@Resolver(() => RowLevelPermissionPredicateGroupDTO) +@UseGuards(WorkspaceAuthGuard, NoPermissionGuard) +export class RowLevelPermissionPredicateGroupResolver { + constructor( + private readonly predicateGroupService: RowLevelPermissionPredicateGroupService, + ) {} + + @Query(() => [RowLevelPermissionPredicateGroupDTO]) + async getRowLevelPermissionPredicateGroups( + @AuthWorkspace() workspace: WorkspaceEntity, + @Args('roleId', { type: () => String, nullable: true }) roleId?: string, + ): Promise { + if (roleId) { + return this.predicateGroupService.findByRole(workspace.id, roleId); + } + + return this.predicateGroupService.findByWorkspaceId(workspace.id); + } + + @Mutation(() => RowLevelPermissionPredicateGroupDTO) + async createRowLevelPermissionPredicateGroup( + @Args('input') + input: CreateRowLevelPermissionPredicateGroupInput, + @AuthWorkspace() workspace: WorkspaceEntity, + ): Promise { + return this.predicateGroupService.createOne({ + createRowLevelPermissionPredicateGroupInput: { + ...input, + logicalOperator: + input.logicalOperator as RowLevelPermissionPredicateGroupLogicalOperator, + }, + workspaceId: workspace.id, + }); + } + + @Mutation(() => RowLevelPermissionPredicateGroupDTO) + async updateRowLevelPermissionPredicateGroup( + @Args('input') + input: UpdateRowLevelPermissionPredicateGroupInput, + @AuthWorkspace() workspace: WorkspaceEntity, + ): Promise { + return this.predicateGroupService.updateOne({ + updateRowLevelPermissionPredicateGroupInput: { + ...input, + logicalOperator: + input.logicalOperator as RowLevelPermissionPredicateGroupLogicalOperator, + }, + workspaceId: workspace.id, + }); + } + + @Mutation(() => RowLevelPermissionPredicateGroupDTO) + async deleteRowLevelPermissionPredicateGroup( + @Args('input') + input: DeleteRowLevelPermissionPredicateGroupInput, + @AuthWorkspace() workspace: WorkspaceEntity, + ): Promise { + return this.predicateGroupService.deleteOne({ + deleteRowLevelPermissionPredicateGroupInput: { + ...input, + }, + workspaceId: workspace.id, + }); + } +} diff --git a/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/resolvers/row-level-permission-predicate.resolver.ts b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/resolvers/row-level-permission-predicate.resolver.ts new file mode 100644 index 0000000000..a45f3de784 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/resolvers/row-level-permission-predicate.resolver.ts @@ -0,0 +1,95 @@ +/* @license Enterprise */ + +import { UseGuards } from '@nestjs/common'; +import { Args, Mutation, Query, Resolver } from '@nestjs/graphql'; + +import { 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 { CreateRowLevelPermissionPredicateInput } from 'src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/create-row-level-permission-predicate.input'; +import { DeleteRowLevelPermissionPredicateInput } from 'src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/delete-row-level-permission-predicate.input'; +import { UpdateRowLevelPermissionPredicateInput } from 'src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/update-row-level-permission-predicate.input'; +import { RowLevelPermissionPredicateDTO } from 'src/engine/metadata-modules/row-level-permission-predicate/dtos/row-level-permission-predicate.dto'; +import { RowLevelPermissionPredicateOperand } from 'src/engine/metadata-modules/row-level-permission-predicate/enums/row-level-permission-predicate-operand'; +import { RowLevelPermissionPredicateService } from 'src/engine/metadata-modules/row-level-permission-predicate/services/row-level-permission-predicate.service'; + +@Resolver(() => RowLevelPermissionPredicateDTO) +@UseGuards(WorkspaceAuthGuard, NoPermissionGuard) +export class RowLevelPermissionPredicateResolver { + constructor( + private readonly predicateService: RowLevelPermissionPredicateService, + ) {} + + @Query(() => [RowLevelPermissionPredicateDTO]) + async getRowLevelPermissionPredicates( + @AuthWorkspace() workspace: WorkspaceEntity, + @Args('roleId', { type: () => String, nullable: true }) roleId?: string, + @Args('objectMetadataId', { type: () => String, nullable: true }) + objectMetadataId?: string, + ): Promise { + if (roleId && objectMetadataId) { + return this.predicateService.findByRoleAndObject( + workspace.id, + roleId, + objectMetadataId, + ); + } + + return this.predicateService.findByWorkspaceId(workspace.id); + } + + @Query(() => RowLevelPermissionPredicateDTO, { nullable: true }) + async getRowLevelPermissionPredicate( + @Args('id', { type: () => String }) id: string, + @AuthWorkspace() workspace: WorkspaceEntity, + ): Promise { + return this.predicateService.findById(id, workspace.id); + } + + @Mutation(() => RowLevelPermissionPredicateDTO) + async createRowLevelPermissionPredicate( + @Args('input') + input: CreateRowLevelPermissionPredicateInput, + @AuthWorkspace() workspace: WorkspaceEntity, + ): Promise { + return this.predicateService.createOne({ + createRowLevelPermissionPredicateInput: { + ...input, + operand: input.operand as RowLevelPermissionPredicateOperand, + value: input.value, + }, + workspaceId: workspace.id, + }); + } + + @Mutation(() => RowLevelPermissionPredicateDTO) + async updateRowLevelPermissionPredicate( + @Args('input') + input: UpdateRowLevelPermissionPredicateInput, + @AuthWorkspace() workspace: WorkspaceEntity, + ): Promise { + return this.predicateService.updateOne({ + updateRowLevelPermissionPredicateInput: { + ...input, + operand: input.operand as RowLevelPermissionPredicateOperand, + value: input.value, + }, + workspaceId: workspace.id, + }); + } + + @Mutation(() => RowLevelPermissionPredicateDTO) + async deleteRowLevelPermissionPredicate( + @Args('input') + input: DeleteRowLevelPermissionPredicateInput, + @AuthWorkspace() workspace: WorkspaceEntity, + ): Promise { + return this.predicateService.deleteOne({ + deleteRowLevelPermissionPredicateInput: { + ...input, + }, + workspaceId: workspace.id, + }); + } +} diff --git a/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/row-level-permission.module.ts b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/row-level-permission.module.ts new file mode 100644 index 0000000000..b703b36488 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/row-level-permission.module.ts @@ -0,0 +1,37 @@ +/* @license Enterprise */ + +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.module'; +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 { RowLevelPermissionPredicateGroupResolver } from 'src/engine/metadata-modules/row-level-permission-predicate/resolvers/row-level-permission-predicate-group.resolver'; +import { RowLevelPermissionPredicateResolver } from 'src/engine/metadata-modules/row-level-permission-predicate/resolvers/row-level-permission-predicate.resolver'; +import { RowLevelPermissionPredicateGroupService } from 'src/engine/metadata-modules/row-level-permission-predicate/services/row-level-permission-predicate-group.service'; +import { RowLevelPermissionPredicateService } from 'src/engine/metadata-modules/row-level-permission-predicate/services/row-level-permission-predicate.service'; +import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache.module'; +import { WorkspaceMigrationV2Module } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-v2.module'; + +@Module({ + imports: [ + TypeOrmModule.forFeature([ + RowLevelPermissionPredicateEntity, + RowLevelPermissionPredicateGroupEntity, + ]), + WorkspaceCacheModule, + WorkspaceManyOrAllFlatEntityMapsCacheModule, + WorkspaceMigrationV2Module, + ], + providers: [ + RowLevelPermissionPredicateService, + RowLevelPermissionPredicateGroupService, + RowLevelPermissionPredicateResolver, + RowLevelPermissionPredicateGroupResolver, + ], + exports: [ + RowLevelPermissionPredicateService, + RowLevelPermissionPredicateGroupService, + ], +}) +export class RowLevelPermissionModule {} diff --git a/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/services/row-level-permission-predicate-group.service.ts b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/services/row-level-permission-predicate-group.service.ts new file mode 100644 index 0000000000..4091108338 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/services/row-level-permission-predicate-group.service.ts @@ -0,0 +1,298 @@ +/* @license Enterprise */ + +import { Injectable } from '@nestjs/common'; + +import { isDefined } from 'twenty-shared/utils'; + +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 { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util'; +import { fromCreateRowLevelPermissionPredicateGroupInputToFlatRowLevelPermissionPredicateGroupToCreate } from 'src/engine/metadata-modules/flat-row-level-permission-predicate-group/utils/from-create-row-level-permission-predicate-group-input-to-flat-row-level-permission-predicate-group-to-create.util'; +import { fromDeleteRowLevelPermissionPredicateGroupInputToFlatRowLevelPermissionPredicateGroupOrThrow } from 'src/engine/metadata-modules/flat-row-level-permission-predicate-group/utils/from-delete-row-level-permission-predicate-group-input-to-flat-row-level-permission-predicate-group-or-throw.util'; +import { fromDestroyRowLevelPermissionPredicateGroupInputToFlatRowLevelPermissionPredicateGroupOrThrow } from 'src/engine/metadata-modules/flat-row-level-permission-predicate-group/utils/from-destroy-row-level-permission-predicate-group-input-to-flat-row-level-permission-predicate-group-or-throw.util'; +import { fromFlatRowLevelPermissionPredicateGroupToDto } from 'src/engine/metadata-modules/flat-row-level-permission-predicate-group/utils/from-flat-row-level-permission-predicate-group-to-dto.util'; +import { fromUpdateRowLevelPermissionPredicateGroupInputToFlatRowLevelPermissionPredicateGroupToUpdateOrThrow } from 'src/engine/metadata-modules/flat-row-level-permission-predicate-group/utils/from-update-row-level-permission-predicate-group-input-to-flat-row-level-permission-predicate-group-to-update-or-throw.util'; +import { type CreateRowLevelPermissionPredicateGroupInput } from 'src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/create-row-level-permission-predicate-group.input'; +import { type DeleteRowLevelPermissionPredicateGroupInput } from 'src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/delete-row-level-permission-predicate-group.input'; +import { type DestroyRowLevelPermissionPredicateGroupInput } from 'src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/destroy-row-level-permission-predicate-group.input'; +import { type UpdateRowLevelPermissionPredicateGroupInput } from 'src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/update-row-level-permission-predicate-group.input'; +import { RowLevelPermissionPredicateGroupDTO } from 'src/engine/metadata-modules/row-level-permission-predicate/dtos/row-level-permission-predicate-group.dto'; +import { type FlatRowLevelPermissionPredicateGroup } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-group.type'; +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'; + +const RLP_CACHE_KEYS = [ + 'flatRowLevelPermissionPredicateMaps', + 'flatRowLevelPermissionPredicateGroupMaps', +] as const; + +@Injectable() +export class RowLevelPermissionPredicateGroupService { + constructor( + private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService, + private readonly flatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService, + ) {} + + async createOne({ + createRowLevelPermissionPredicateGroupInput, + workspaceId, + }: { + createRowLevelPermissionPredicateGroupInput: CreateRowLevelPermissionPredicateGroupInput; + workspaceId: string; + }): Promise { + const flatGroupToCreate = + fromCreateRowLevelPermissionPredicateGroupInputToFlatRowLevelPermissionPredicateGroupToCreate( + { + createRowLevelPermissionPredicateGroupInput, + workspaceId, + }, + ); + + await this.runMigration({ + workspaceId, + flatEntityToCreate: [flatGroupToCreate], + }); + + const { flatRowLevelPermissionPredicateGroupMaps } = + await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps( + { + workspaceId, + flatMapsKeys: ['flatRowLevelPermissionPredicateGroupMaps'], + }, + ); + + return fromFlatRowLevelPermissionPredicateGroupToDto( + findFlatEntityByIdInFlatEntityMapsOrThrow({ + flatEntityId: flatGroupToCreate.id, + flatEntityMaps: flatRowLevelPermissionPredicateGroupMaps, + }), + ); + } + + async updateOne({ + updateRowLevelPermissionPredicateGroupInput, + workspaceId, + }: { + updateRowLevelPermissionPredicateGroupInput: UpdateRowLevelPermissionPredicateGroupInput; + workspaceId: string; + }): Promise { + const { flatRowLevelPermissionPredicateGroupMaps } = + await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps( + { + workspaceId, + flatMapsKeys: ['flatRowLevelPermissionPredicateGroupMaps'], + }, + ); + + const flatGroupToUpdate = + fromUpdateRowLevelPermissionPredicateGroupInputToFlatRowLevelPermissionPredicateGroupToUpdateOrThrow( + { + flatRowLevelPermissionPredicateGroupMaps, + updateRowLevelPermissionPredicateGroupInput, + }, + ); + + await this.runMigration({ + workspaceId, + flatEntityToUpdate: [flatGroupToUpdate], + }); + + const { flatRowLevelPermissionPredicateGroupMaps: recomputedFlatMaps } = + await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps( + { + workspaceId, + flatMapsKeys: ['flatRowLevelPermissionPredicateGroupMaps'], + }, + ); + + return fromFlatRowLevelPermissionPredicateGroupToDto( + findFlatEntityByIdInFlatEntityMapsOrThrow({ + flatEntityId: flatGroupToUpdate.id, + flatEntityMaps: recomputedFlatMaps, + }), + ); + } + + async deleteOne({ + deleteRowLevelPermissionPredicateGroupInput, + workspaceId, + }: { + deleteRowLevelPermissionPredicateGroupInput: DeleteRowLevelPermissionPredicateGroupInput; + workspaceId: string; + }): Promise { + const { flatRowLevelPermissionPredicateGroupMaps } = + await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps( + { + workspaceId, + flatMapsKeys: ['flatRowLevelPermissionPredicateGroupMaps'], + }, + ); + + const flatGroupWithDeletedAt = + fromDeleteRowLevelPermissionPredicateGroupInputToFlatRowLevelPermissionPredicateGroupOrThrow( + { + deleteRowLevelPermissionPredicateGroupInput, + flatRowLevelPermissionPredicateGroupMaps, + }, + ); + + await this.runMigration({ + workspaceId, + flatEntityToUpdate: [flatGroupWithDeletedAt], + }); + + const { flatRowLevelPermissionPredicateGroupMaps: recomputedFlatMaps } = + await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps( + { + workspaceId, + flatMapsKeys: ['flatRowLevelPermissionPredicateGroupMaps'], + }, + ); + + return fromFlatRowLevelPermissionPredicateGroupToDto( + findFlatEntityByIdInFlatEntityMapsOrThrow({ + flatEntityId: flatGroupWithDeletedAt.id, + flatEntityMaps: recomputedFlatMaps, + }), + ); + } + + async destroyOne({ + destroyRowLevelPermissionPredicateGroupInput, + workspaceId, + }: { + destroyRowLevelPermissionPredicateGroupInput: DestroyRowLevelPermissionPredicateGroupInput; + workspaceId: string; + }): Promise { + const { flatRowLevelPermissionPredicateGroupMaps } = + await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps( + { + workspaceId, + flatMapsKeys: ['flatRowLevelPermissionPredicateGroupMaps'], + }, + ); + + const flatGroupToDelete = + fromDestroyRowLevelPermissionPredicateGroupInputToFlatRowLevelPermissionPredicateGroupOrThrow( + { + destroyRowLevelPermissionPredicateGroupInput, + flatRowLevelPermissionPredicateGroupMaps, + }, + ); + + await this.runMigration({ + workspaceId, + flatEntityToDelete: [flatGroupToDelete], + }); + + return fromFlatRowLevelPermissionPredicateGroupToDto(flatGroupToDelete); + } + + async findByWorkspaceId( + workspaceId: string, + ): Promise { + const { flatRowLevelPermissionPredicateGroupMaps } = + await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps( + { + workspaceId, + flatMapsKeys: ['flatRowLevelPermissionPredicateGroupMaps'], + }, + ); + + return Object.values(flatRowLevelPermissionPredicateGroupMaps.byId) + .filter(isDefined) + .filter((group) => group.deletedAt === null) + .sort( + (a, b) => + (a.positionInRowLevelPermissionPredicateGroup ?? 0) - + (b.positionInRowLevelPermissionPredicateGroup ?? 0), + ) + .map(fromFlatRowLevelPermissionPredicateGroupToDto); + } + + async findByRole( + workspaceId: string, + roleId: string, + ): Promise { + const { flatRowLevelPermissionPredicateGroupMaps } = + await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps( + { + workspaceId, + flatMapsKeys: ['flatRowLevelPermissionPredicateGroupMaps'], + }, + ); + + return Object.values(flatRowLevelPermissionPredicateGroupMaps.byId) + .filter(isDefined) + .filter((group) => group.deletedAt === null && group.roleId === roleId) + .sort( + (a, b) => + (a.positionInRowLevelPermissionPredicateGroup ?? 0) - + (b.positionInRowLevelPermissionPredicateGroup ?? 0), + ) + .map(fromFlatRowLevelPermissionPredicateGroupToDto); + } + + async findById( + id: string, + workspaceId: string, + ): Promise { + const { flatRowLevelPermissionPredicateGroupMaps } = + await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps( + { + workspaceId, + flatMapsKeys: ['flatRowLevelPermissionPredicateGroupMaps'], + }, + ); + + const flatGroup = findFlatEntityByIdInFlatEntityMaps({ + flatEntityId: id, + flatEntityMaps: flatRowLevelPermissionPredicateGroupMaps, + }); + + if (!isDefined(flatGroup) || flatGroup.deletedAt !== null) { + return null; + } + + return fromFlatRowLevelPermissionPredicateGroupToDto(flatGroup); + } + + private async runMigration({ + workspaceId, + flatEntityToCreate = [], + flatEntityToUpdate = [], + flatEntityToDelete = [], + }: { + workspaceId: string; + flatEntityToCreate?: FlatRowLevelPermissionPredicateGroup[]; + flatEntityToUpdate?: FlatRowLevelPermissionPredicateGroup[]; + flatEntityToDelete?: FlatRowLevelPermissionPredicateGroup[]; + }): Promise { + const validateAndBuildResult = + await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration( + { + allFlatEntityOperationByMetadataName: { + rowLevelPermissionPredicateGroup: { + flatEntityToCreate, + flatEntityToDelete, + flatEntityToUpdate, + }, + }, + workspaceId, + isSystemBuild: false, + }, + ); + + if (isDefined(validateAndBuildResult)) { + throw new WorkspaceMigrationBuilderExceptionV2( + validateAndBuildResult, + 'Validation errors occurred while applying row level permission predicate group mutation', + ); + } + + await this.flatEntityMapsCacheService.invalidateFlatEntityMaps({ + workspaceId, + flatMapsKeys: [...RLP_CACHE_KEYS], + }); + } +} diff --git a/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/services/row-level-permission-predicate.service.ts b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/services/row-level-permission-predicate.service.ts new file mode 100644 index 0000000000..5f3309a85d --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/services/row-level-permission-predicate.service.ts @@ -0,0 +1,304 @@ +/* @license Enterprise */ + +import { Injectable } from '@nestjs/common'; + +import { isDefined } from 'twenty-shared/utils'; + +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 { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util'; +import { fromCreateRowLevelPermissionPredicateInputToFlatRowLevelPermissionPredicateToCreate } from 'src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-create-row-level-permission-predicate-input-to-flat-row-level-permission-predicate-to-create.util'; +import { fromDeleteRowLevelPermissionPredicateInputToFlatRowLevelPermissionPredicateOrThrow } from 'src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-delete-row-level-permission-predicate-input-to-flat-row-level-permission-predicate-or-throw.util'; +import { fromDestroyRowLevelPermissionPredicateInputToFlatRowLevelPermissionPredicateOrThrow } from 'src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-destroy-row-level-permission-predicate-input-to-flat-row-level-permission-predicate-or-throw.util'; +import { fromFlatRowLevelPermissionPredicateToDto } from 'src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-flat-row-level-permission-predicate-to-dto.util'; +import { fromUpdateRowLevelPermissionPredicateInputToFlatRowLevelPermissionPredicateToUpdateOrThrow } from 'src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-update-row-level-permission-predicate-input-to-flat-row-level-permission-predicate-to-update-or-throw.util'; +import { type CreateRowLevelPermissionPredicateInput } from 'src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/create-row-level-permission-predicate.input'; +import { type DeleteRowLevelPermissionPredicateInput } from 'src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/delete-row-level-permission-predicate.input'; +import { type DestroyRowLevelPermissionPredicateInput } from 'src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/destroy-row-level-permission-predicate.input'; +import { type UpdateRowLevelPermissionPredicateInput } from 'src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/update-row-level-permission-predicate.input'; +import { RowLevelPermissionPredicateDTO } from 'src/engine/metadata-modules/row-level-permission-predicate/dtos/row-level-permission-predicate.dto'; +import { type FlatRowLevelPermissionPredicate } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate.type'; +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'; + +const RLP_CACHE_KEYS = [ + 'flatRowLevelPermissionPredicateMaps', + 'flatRowLevelPermissionPredicateGroupMaps', +] as const; + +@Injectable() +export class RowLevelPermissionPredicateService { + constructor( + private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService, + private readonly flatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService, + ) {} + + async createOne({ + createRowLevelPermissionPredicateInput, + workspaceId, + }: { + createRowLevelPermissionPredicateInput: CreateRowLevelPermissionPredicateInput; + workspaceId: string; + }): Promise { + const flatPredicateToCreate = + fromCreateRowLevelPermissionPredicateInputToFlatRowLevelPermissionPredicateToCreate( + { + createRowLevelPermissionPredicateInput, + workspaceId, + }, + ); + + await this.runMigration({ + workspaceId, + flatEntityToCreate: [flatPredicateToCreate], + }); + + const { flatRowLevelPermissionPredicateMaps } = + await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps( + { + workspaceId, + flatMapsKeys: ['flatRowLevelPermissionPredicateMaps'], + }, + ); + + return fromFlatRowLevelPermissionPredicateToDto( + findFlatEntityByIdInFlatEntityMapsOrThrow({ + flatEntityId: flatPredicateToCreate.id, + flatEntityMaps: flatRowLevelPermissionPredicateMaps, + }), + ); + } + + async updateOne({ + updateRowLevelPermissionPredicateInput, + workspaceId, + }: { + updateRowLevelPermissionPredicateInput: UpdateRowLevelPermissionPredicateInput; + workspaceId: string; + }): Promise { + const { flatRowLevelPermissionPredicateMaps } = + await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps( + { + workspaceId, + flatMapsKeys: ['flatRowLevelPermissionPredicateMaps'], + }, + ); + + const flatPredicateToUpdate = + fromUpdateRowLevelPermissionPredicateInputToFlatRowLevelPermissionPredicateToUpdateOrThrow( + { + flatRowLevelPermissionPredicateMaps, + updateRowLevelPermissionPredicateInput, + }, + ); + + await this.runMigration({ + workspaceId, + flatEntityToUpdate: [flatPredicateToUpdate], + }); + + const { flatRowLevelPermissionPredicateMaps: recomputedFlatMaps } = + await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps( + { + workspaceId, + flatMapsKeys: ['flatRowLevelPermissionPredicateMaps'], + }, + ); + + return fromFlatRowLevelPermissionPredicateToDto( + findFlatEntityByIdInFlatEntityMapsOrThrow({ + flatEntityId: flatPredicateToUpdate.id, + flatEntityMaps: recomputedFlatMaps, + }), + ); + } + + async deleteOne({ + deleteRowLevelPermissionPredicateInput, + workspaceId, + }: { + deleteRowLevelPermissionPredicateInput: DeleteRowLevelPermissionPredicateInput; + workspaceId: string; + }): Promise { + const { flatRowLevelPermissionPredicateMaps } = + await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps( + { + workspaceId, + flatMapsKeys: ['flatRowLevelPermissionPredicateMaps'], + }, + ); + + const flatPredicateWithDeletedAt = + fromDeleteRowLevelPermissionPredicateInputToFlatRowLevelPermissionPredicateOrThrow( + { + deleteRowLevelPermissionPredicateInput, + flatRowLevelPermissionPredicateMaps, + }, + ); + + await this.runMigration({ + workspaceId, + flatEntityToUpdate: [flatPredicateWithDeletedAt], + }); + + const { flatRowLevelPermissionPredicateMaps: recomputedFlatMaps } = + await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps( + { + workspaceId, + flatMapsKeys: ['flatRowLevelPermissionPredicateMaps'], + }, + ); + + return fromFlatRowLevelPermissionPredicateToDto( + findFlatEntityByIdInFlatEntityMapsOrThrow({ + flatEntityId: flatPredicateWithDeletedAt.id, + flatEntityMaps: recomputedFlatMaps, + }), + ); + } + + async destroyOne({ + destroyRowLevelPermissionPredicateInput, + workspaceId, + }: { + destroyRowLevelPermissionPredicateInput: DestroyRowLevelPermissionPredicateInput; + workspaceId: string; + }): Promise { + const { flatRowLevelPermissionPredicateMaps } = + await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps( + { + workspaceId, + flatMapsKeys: ['flatRowLevelPermissionPredicateMaps'], + }, + ); + + const flatPredicateToDelete = + fromDestroyRowLevelPermissionPredicateInputToFlatRowLevelPermissionPredicateOrThrow( + { + destroyRowLevelPermissionPredicateInput, + flatRowLevelPermissionPredicateMaps, + }, + ); + + await this.runMigration({ + workspaceId, + flatEntityToDelete: [flatPredicateToDelete], + }); + + return fromFlatRowLevelPermissionPredicateToDto(flatPredicateToDelete); + } + + async findByWorkspaceId( + workspaceId: string, + ): Promise { + const { flatRowLevelPermissionPredicateMaps } = + await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps( + { + workspaceId, + flatMapsKeys: ['flatRowLevelPermissionPredicateMaps'], + }, + ); + + return Object.values(flatRowLevelPermissionPredicateMaps.byId) + .filter(isDefined) + .filter((predicate) => predicate.deletedAt === null) + .sort( + (a, b) => + (a.positionInRowLevelPermissionPredicateGroup ?? 0) - + (b.positionInRowLevelPermissionPredicateGroup ?? 0), + ) + .map(fromFlatRowLevelPermissionPredicateToDto); + } + + async findByRoleAndObject( + workspaceId: string, + roleId: string, + objectMetadataId: string, + ): Promise { + const { flatRowLevelPermissionPredicateMaps } = + await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps( + { + workspaceId, + flatMapsKeys: ['flatRowLevelPermissionPredicateMaps'], + }, + ); + + return Object.values(flatRowLevelPermissionPredicateMaps.byId) + .filter(isDefined) + .filter( + (predicate) => + predicate.deletedAt === null && + predicate.roleId === roleId && + predicate.objectMetadataId === objectMetadataId, + ) + .sort( + (a, b) => + (a.positionInRowLevelPermissionPredicateGroup ?? 0) - + (b.positionInRowLevelPermissionPredicateGroup ?? 0), + ) + .map(fromFlatRowLevelPermissionPredicateToDto); + } + + async findById( + id: string, + workspaceId: string, + ): Promise { + const { flatRowLevelPermissionPredicateMaps } = + await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps( + { + workspaceId, + flatMapsKeys: ['flatRowLevelPermissionPredicateMaps'], + }, + ); + + const flatPredicate = findFlatEntityByIdInFlatEntityMaps({ + flatEntityId: id, + flatEntityMaps: flatRowLevelPermissionPredicateMaps, + }); + + if (!isDefined(flatPredicate) || flatPredicate.deletedAt !== null) { + return null; + } + + return fromFlatRowLevelPermissionPredicateToDto(flatPredicate); + } + + private async runMigration({ + workspaceId, + flatEntityToCreate = [], + flatEntityToUpdate = [], + flatEntityToDelete = [], + }: { + workspaceId: string; + flatEntityToCreate?: FlatRowLevelPermissionPredicate[]; + flatEntityToUpdate?: FlatRowLevelPermissionPredicate[]; + flatEntityToDelete?: FlatRowLevelPermissionPredicate[]; + }): Promise { + const validateAndBuildResult = + await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration( + { + allFlatEntityOperationByMetadataName: { + rowLevelPermissionPredicate: { + flatEntityToCreate, + flatEntityToDelete, + flatEntityToUpdate, + }, + }, + workspaceId, + isSystemBuild: false, + }, + ); + + if (isDefined(validateAndBuildResult)) { + throw new WorkspaceMigrationBuilderExceptionV2( + validateAndBuildResult, + 'Validation errors occurred while applying row level permission predicate mutation', + ); + } + + await this.flatEntityMapsCacheService.invalidateFlatEntityMaps({ + workspaceId, + flatMapsKeys: [...RLP_CACHE_KEYS], + }); + } +} diff --git a/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-group-maps.type.ts b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-group-maps.type.ts new file mode 100644 index 0000000000..de2a66c2cd --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-group-maps.type.ts @@ -0,0 +1,7 @@ +/* @license Enterprise */ + +import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type'; +import { type FlatRowLevelPermissionPredicateGroup } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-group.type'; + +export type FlatRowLevelPermissionPredicateGroupMaps = + FlatEntityMaps; diff --git a/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-group.type.ts b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-group.type.ts new file mode 100644 index 0000000000..7ee4285a67 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-group.type.ts @@ -0,0 +1,10 @@ +/* @license Enterprise */ + +import { type FlatEntityFrom } from 'src/engine/metadata-modules/flat-entity/types/flat-entity.type'; +import { type RowLevelPermissionPredicateGroupEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate-group.entity'; + +export type FlatRowLevelPermissionPredicateGroup = + FlatEntityFrom & { + childRowLevelPermissionPredicateGroupIds: string[]; + rowLevelPermissionPredicateIds: string[]; + }; diff --git a/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-maps.type.ts b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-maps.type.ts new file mode 100644 index 0000000000..53026ca8ce --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-maps.type.ts @@ -0,0 +1,7 @@ +/* @license Enterprise */ + +import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type'; +import { type FlatRowLevelPermissionPredicate } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate.type'; + +export type FlatRowLevelPermissionPredicateMaps = + FlatEntityMaps; diff --git a/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate.type.ts b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate.type.ts new file mode 100644 index 0000000000..09f5deace6 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate.type.ts @@ -0,0 +1,7 @@ +/* @license Enterprise */ + +import { type FlatEntityFrom } from 'src/engine/metadata-modules/flat-entity/types/flat-entity.type'; +import { type RowLevelPermissionPredicateEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate.entity'; + +export type FlatRowLevelPermissionPredicate = + FlatEntityFrom; diff --git a/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/types/row-level-permission-predicate-value.type.ts b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/types/row-level-permission-predicate-value.type.ts new file mode 100644 index 0000000000..171ffc7e6f --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/types/row-level-permission-predicate-value.type.ts @@ -0,0 +1,16 @@ +/* @license Enterprise */ + +export type RelationPredicateValue = { + isCurrentWorkspaceMemberSelected?: boolean; + selectedRecordIds: string[]; +}; + +export type RowLevelPermissionPredicateValue = + | string + | string[] + | boolean + | number + | RelationPredicateValue + | Record + | null + | undefined; diff --git a/packages/twenty-server/src/engine/middlewares/middleware.service.ts b/packages/twenty-server/src/engine/middlewares/middleware.service.ts index 71bd0e8094..6b7854c31b 100644 --- a/packages/twenty-server/src/engine/middlewares/middleware.service.ts +++ b/packages/twenty-server/src/engine/middlewares/middleware.service.ts @@ -14,13 +14,13 @@ import { JwtWrapperService } from 'src/engine/core-modules/jwt/services/jwt-wrap import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service'; import { INTERNAL_SERVER_ERROR } from 'src/engine/middlewares/constants/default-error-message.constant'; +import { bindDataToRequestObject } from 'src/engine/utils/bind-data-to-request-object.util'; import { handleException, handleExceptionAndConvertToGraphQLError, } from 'src/engine/utils/global-exception-handler.util'; import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service'; import { type CustomException } from 'src/utils/custom-exception'; -import { bindDataToRequestObject } from 'src/engine/utils/bind-data-to-request-object.util'; @Injectable() export class MiddlewareService { diff --git a/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.spec.ts b/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.spec.ts index 45f0d2af3f..db684d7c8e 100644 --- a/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.spec.ts +++ b/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.spec.ts @@ -193,6 +193,16 @@ describe('WorkspaceEntityManager', () => { idByUniversalIdentifier: {}, universalIdentifiersByApplicationId: {}, }, + flatRowLevelPermissionPredicateMaps: { + byId: {}, + idByUniversalIdentifier: {}, + universalIdentifiersByApplicationId: {}, + }, + flatRowLevelPermissionPredicateGroupMaps: { + byId: {}, + idByUniversalIdentifier: {}, + universalIdentifiersByApplicationId: {}, + }, objectIdByNameSingular: { 'test-entity': 'test-entity-id', }, @@ -213,8 +223,10 @@ describe('WorkspaceEntityManager', () => { IS_DASHBOARD_V2_ENABLED: false, IS_TIMELINE_ACTIVITY_MIGRATED: false, IS_GLOBAL_WORKSPACE_DATASOURCE_ENABLED: false, + IS_ROW_LEVEL_PERMISSION_PREDICATES_ENABLED: false, IS_WORKSPACE_CREATION_V2_ENABLED: false, }, + userWorkspaceRoleMap: {}, eventEmitterService: { emitMutationEvent: jest.fn(), emitDatabaseBatchEvent: jest.fn(), @@ -238,6 +250,7 @@ describe('WorkspaceEntityManager', () => { IS_WORKFLOW_RUN_STOPPAGE_ENABLED: false, IS_DASHBOARD_V2_ENABLED: false, IS_GLOBAL_WORKSPACE_DATASOURCE_ENABLED: false, + IS_ROW_LEVEL_PERMISSION_PREDICATES_ENABLED: false, }, permissionsPerRoleId: {}, eventEmitterService: mockInternalContext.eventEmitterService, @@ -269,6 +282,10 @@ describe('WorkspaceEntityManager', () => { flatObjectMetadataMaps, flatFieldMetadataMaps, flatIndexMaps: mockInternalContext.flatIndexMaps, + flatRowLevelPermissionPredicateMaps: + mockInternalContext.flatRowLevelPermissionPredicateMaps, + flatRowLevelPermissionPredicateGroupMaps: + mockInternalContext.flatRowLevelPermissionPredicateGroupMaps, objectIdByNameSingular: mockInternalContext.objectIdByNameSingular, featureFlagsMap: mockInternalContext.featureFlagsMap, permissionsPerRoleId: mockDataSource.permissionsPerRoleId, diff --git a/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.ts b/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.ts index 235e8486b8..deca11125a 100644 --- a/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.ts +++ b/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.ts @@ -33,6 +33,7 @@ import { PlainObjectToDatabaseEntityTransformer } from 'typeorm/query-builder/tr import { type UpsertOptions } from 'typeorm/repository/UpsertOptions'; import { InstanceChecker } from 'typeorm/util/InstanceChecker'; +import { type WorkspaceAuthContext } from 'src/engine/api/common/interfaces/workspace-auth-context.interface'; import { type FeatureFlagMap } from 'src/engine/core-modules/feature-flag/interfaces/feature-flag-map.interface'; import { type WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/workspace-internal-context.interface'; @@ -87,6 +88,12 @@ export class WorkspaceEntityManager extends EntityManager { return this.connection.eventEmitterService; } + get authContext(): WorkspaceAuthContext { + const context = getWorkspaceContext(); + + return context.authContext; + } + get internalContext(): WorkspaceInternalContext { const context = getWorkspaceContext(); @@ -95,8 +102,13 @@ export class WorkspaceEntityManager extends EntityManager { flatObjectMetadataMaps: context.flatObjectMetadataMaps, flatFieldMetadataMaps: context.flatFieldMetadataMaps, flatIndexMaps: context.flatIndexMaps, + flatRowLevelPermissionPredicateMaps: + context.flatRowLevelPermissionPredicateMaps, + flatRowLevelPermissionPredicateGroupMaps: + context.flatRowLevelPermissionPredicateGroupMaps, objectIdByNameSingular: context.objectIdByNameSingular, featureFlagsMap: context.featureFlagsMap, + userWorkspaceRoleMap: context.userWorkspaceRoleMap, eventEmitterService: this.eventEmitterService, }; } @@ -211,7 +223,7 @@ export class WorkspaceEntityManager extends EntityManager { options?.objectRecordsPermissions ?? {}, this.internalContext, options?.shouldBypassPermissionChecks ?? false, - {}, + this.authContext, this.getFeatureFlagMap(), ); } diff --git a/packages/twenty-server/src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager.ts b/packages/twenty-server/src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager.ts index c0cf48eb78..57d061670c 100644 --- a/packages/twenty-server/src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager.ts +++ b/packages/twenty-server/src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager.ts @@ -88,6 +88,8 @@ export class GlobalWorkspaceOrmManager { rolesPermissions: permissionsPerRoleId, ORMEntityMetadatas: entityMetadatas, userWorkspaceRoleMap, + flatRowLevelPermissionPredicateMaps, + flatRowLevelPermissionPredicateGroupMaps, } = await this.workspaceCacheService.getOrRecompute(workspaceId, [ 'flatObjectMetadataMaps', 'flatFieldMetadataMaps', @@ -96,6 +98,8 @@ export class GlobalWorkspaceOrmManager { 'rolesPermissions', 'ORMEntityMetadatas', 'userWorkspaceRoleMap', + 'flatRowLevelPermissionPredicateMaps', + 'flatRowLevelPermissionPredicateGroupMaps', ]); const { idByNameSingular: objectIdByNameSingular } = @@ -106,6 +110,8 @@ export class GlobalWorkspaceOrmManager { flatObjectMetadataMaps, flatFieldMetadataMaps, flatIndexMaps, + flatRowLevelPermissionPredicateMaps, + flatRowLevelPermissionPredicateGroupMaps, objectIdByNameSingular, featureFlagsMap, permissionsPerRoleId, diff --git a/packages/twenty-server/src/engine/twenty-orm/interfaces/workspace-internal-context.interface.ts b/packages/twenty-server/src/engine/twenty-orm/interfaces/workspace-internal-context.interface.ts index 57a66bdf80..aff02b1434 100644 --- a/packages/twenty-server/src/engine/twenty-orm/interfaces/workspace-internal-context.interface.ts +++ b/packages/twenty-server/src/engine/twenty-orm/interfaces/workspace-internal-context.interface.ts @@ -3,6 +3,8 @@ import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/typ import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type'; import { type FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type'; import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type'; +import { type FlatRowLevelPermissionPredicateGroupMaps } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-group-maps.type'; +import { type FlatRowLevelPermissionPredicateMaps } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-maps.type'; import { type WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter'; export interface WorkspaceInternalContext { @@ -10,7 +12,10 @@ export interface WorkspaceInternalContext { flatObjectMetadataMaps: FlatEntityMaps; flatFieldMetadataMaps: FlatEntityMaps; flatIndexMaps: FlatEntityMaps; + flatRowLevelPermissionPredicateMaps: FlatRowLevelPermissionPredicateMaps; + flatRowLevelPermissionPredicateGroupMaps: FlatRowLevelPermissionPredicateGroupMaps; objectIdByNameSingular: Record; featureFlagsMap: Record; + userWorkspaceRoleMap: Record; eventEmitterService: WorkspaceEventEmitter; } diff --git a/packages/twenty-server/src/engine/twenty-orm/repository/__tests__/workspace.repository.spec.ts b/packages/twenty-server/src/engine/twenty-orm/repository/__tests__/workspace.repository.spec.ts index 413bb44246..43f8972f37 100644 --- a/packages/twenty-server/src/engine/twenty-orm/repository/__tests__/workspace.repository.spec.ts +++ b/packages/twenty-server/src/engine/twenty-orm/repository/__tests__/workspace.repository.spec.ts @@ -118,8 +118,19 @@ describe('WorkspaceRepository', () => { idByUniversalIdentifier: {}, universalIdentifiersByApplicationId: {}, }, + flatRowLevelPermissionPredicateMaps: { + byId: {}, + idByUniversalIdentifier: {}, + universalIdentifiersByApplicationId: {}, + }, + flatRowLevelPermissionPredicateGroupMaps: { + byId: {}, + idByUniversalIdentifier: {}, + universalIdentifiersByApplicationId: {}, + }, objectIdByNameSingular: {}, - featureFlagsMap: {}, + featureFlagsMap: {} as FeatureFlagMap, + userWorkspaceRoleMap: {}, eventEmitterService: {} as unknown, } as unknown as WorkspaceInternalContext; diff --git a/packages/twenty-server/src/engine/twenty-orm/repository/workspace-delete-query-builder.ts b/packages/twenty-server/src/engine/twenty-orm/repository/workspace-delete-query-builder.ts index 1ac5711008..7b00df30e4 100644 --- a/packages/twenty-server/src/engine/twenty-orm/repository/workspace-delete-query-builder.ts +++ b/packages/twenty-server/src/engine/twenty-orm/repository/workspace-delete-query-builder.ts @@ -23,6 +23,7 @@ import { validateQueryIsPermittedOrThrow } from 'src/engine/twenty-orm/repositor import { type WorkspaceSelectQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-select-query-builder'; import { type WorkspaceSoftDeleteQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-soft-delete-query-builder'; import { type WorkspaceUpdateQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-update-query-builder'; +import { applyRowLevelPermissionPredicates } from 'src/engine/twenty-orm/utils/apply-row-level-permission-predicates.util'; import { applyTableAliasOnWhereCondition } from 'src/engine/twenty-orm/utils/apply-table-alias-on-where-condition'; import { computeEventSelectQueryBuilder } from 'src/engine/twenty-orm/utils/compute-event-select-query-builder.util'; import { formatResult } from 'src/engine/twenty-orm/utils/format-result.util'; @@ -57,7 +58,7 @@ export class WorkspaceDeleteQueryBuilder< override clone(): this { const clonedQueryBuilder = super.clone(); - return new WorkspaceDeleteQueryBuilder( + const workspaceDeleteQueryBuilder = new WorkspaceDeleteQueryBuilder( clonedQueryBuilder, this.objectRecordsPermissions, this.internalContext, @@ -65,10 +66,13 @@ export class WorkspaceDeleteQueryBuilder< this.authContext, this.featureFlagMap, ) as this; + + return workspaceDeleteQueryBuilder; } override async execute(): Promise { try { + this.applyRowLevelPermissionPredicates(); validateQueryIsPermittedOrThrow({ expressionMap: this.expressionMap, objectsPermissions: this.objectRecordsPermissions, @@ -157,6 +161,27 @@ export class WorkspaceDeleteQueryBuilder< return mainAliasTarget; } + private applyRowLevelPermissionPredicates(): void { + if (this.shouldBypassPermissionChecks) { + return; + } + + const mainAliasTarget = this.getMainAliasTarget(); + + const objectMetadata = getObjectMetadataFromEntityTarget( + mainAliasTarget, + this.internalContext, + ); + + applyRowLevelPermissionPredicates({ + queryBuilder: this as unknown as WorkspaceSelectQueryBuilder, + objectMetadata, + internalContext: this.internalContext, + authContext: this.authContext, + featureFlagMap: this.featureFlagMap, + }); + } + override select(): WorkspaceSelectQueryBuilder { throw new TwentyORMException( 'This builder cannot morph into a select builder', diff --git a/packages/twenty-server/src/engine/twenty-orm/repository/workspace-select-query-builder.ts b/packages/twenty-server/src/engine/twenty-orm/repository/workspace-select-query-builder.ts index 8a83b9096b..8308c02754 100644 --- a/packages/twenty-server/src/engine/twenty-orm/repository/workspace-select-query-builder.ts +++ b/packages/twenty-server/src/engine/twenty-orm/repository/workspace-select-query-builder.ts @@ -24,6 +24,7 @@ import { WorkspaceDeleteQueryBuilder } from 'src/engine/twenty-orm/repository/wo import { WorkspaceInsertQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-insert-query-builder'; import { WorkspaceSoftDeleteQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-soft-delete-query-builder'; import { WorkspaceUpdateQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-update-query-builder'; +import { applyRowLevelPermissionPredicates } from 'src/engine/twenty-orm/utils/apply-row-level-permission-predicates.util'; import { formatResult } from 'src/engine/twenty-orm/utils/format-result.util'; import { getObjectMetadataFromEntityTarget } from 'src/engine/twenty-orm/utils/get-object-metadata-from-entity-target.util'; @@ -58,7 +59,7 @@ export class WorkspaceSelectQueryBuilder< override clone(): this { const clonedQueryBuilder = super.clone(); - return new WorkspaceSelectQueryBuilder( + const workspaceSelectQueryBuilder = new WorkspaceSelectQueryBuilder( clonedQueryBuilder, this.objectRecordsPermissions, this.internalContext, @@ -66,6 +67,8 @@ export class WorkspaceSelectQueryBuilder< this.authContext, this.featureFlagMap, ) as this; + + return workspaceSelectQueryBuilder; } // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -327,6 +330,7 @@ export class WorkspaceSelectQueryBuilder< } private validatePermissions(): void { + this.applyRowLevelPermissionPredicates(); validateQueryIsPermittedOrThrow({ expressionMap: this.expressionMap, objectsPermissions: this.objectRecordsPermissions, @@ -338,7 +342,9 @@ export class WorkspaceSelectQueryBuilder< } private getMainAliasTarget(): EntityTarget { - const mainAliasTarget = this.expressionMap.mainAlias?.target; + const mainAlias = this.expressionMap.mainAlias; + + const mainAliasTarget = mainAlias?.target; if (!mainAliasTarget) { throw new TwentyORMException( @@ -349,4 +355,31 @@ export class WorkspaceSelectQueryBuilder< return mainAliasTarget; } + + private applyRowLevelPermissionPredicates(): void { + if (this.shouldBypassPermissionChecks) { + return; + } + + // Subqueries don't have entity metadata, skip permission predicates + // Permissions are already applied on the original entity query + if (this.expressionMap.mainAlias?.subQuery) { + return; + } + + const mainAliasTarget = this.getMainAliasTarget(); + + const objectMetadata = getObjectMetadataFromEntityTarget( + mainAliasTarget, + this.internalContext, + ); + + applyRowLevelPermissionPredicates({ + queryBuilder: this, + objectMetadata, + internalContext: this.internalContext, + authContext: this.authContext, + featureFlagMap: this.featureFlagMap, + }); + } } diff --git a/packages/twenty-server/src/engine/twenty-orm/repository/workspace-soft-delete-query-builder.ts b/packages/twenty-server/src/engine/twenty-orm/repository/workspace-soft-delete-query-builder.ts index e5c21cd0ed..b362df1e02 100644 --- a/packages/twenty-server/src/engine/twenty-orm/repository/workspace-soft-delete-query-builder.ts +++ b/packages/twenty-server/src/engine/twenty-orm/repository/workspace-soft-delete-query-builder.ts @@ -22,6 +22,7 @@ import { validateQueryIsPermittedOrThrow } from 'src/engine/twenty-orm/repositor import { type WorkspaceDeleteQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-delete-query-builder'; import { type WorkspaceSelectQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-select-query-builder'; import { type WorkspaceUpdateQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-update-query-builder'; +import { applyRowLevelPermissionPredicates } from 'src/engine/twenty-orm/utils/apply-row-level-permission-predicates.util'; import { applyTableAliasOnWhereCondition } from 'src/engine/twenty-orm/utils/apply-table-alias-on-where-condition'; import { computeEventSelectQueryBuilder } from 'src/engine/twenty-orm/utils/compute-event-select-query-builder.util'; import { formatResult } from 'src/engine/twenty-orm/utils/format-result.util'; @@ -57,7 +58,7 @@ export class WorkspaceSoftDeleteQueryBuilder< override clone(): this { const clonedQueryBuilder = super.clone(); - return new WorkspaceSoftDeleteQueryBuilder( + const workspaceSoftDeleteQueryBuilder = new WorkspaceSoftDeleteQueryBuilder( clonedQueryBuilder, this.objectRecordsPermissions, this.internalContext, @@ -65,10 +66,13 @@ export class WorkspaceSoftDeleteQueryBuilder< this.authContext, this.featureFlagMap, ) as this; + + return workspaceSoftDeleteQueryBuilder; } override async execute(): Promise { try { + this.applyRowLevelPermissionPredicates(); validateQueryIsPermittedOrThrow({ expressionMap: this.expressionMap, objectsPermissions: this.objectRecordsPermissions, @@ -184,4 +188,25 @@ export class WorkspaceSoftDeleteQueryBuilder< return mainAliasTarget; } + + private applyRowLevelPermissionPredicates(): void { + if (this.shouldBypassPermissionChecks) { + return; + } + + const mainAliasTarget = this.getMainAliasTarget(); + + const objectMetadata = getObjectMetadataFromEntityTarget( + mainAliasTarget, + this.internalContext, + ); + + applyRowLevelPermissionPredicates({ + queryBuilder: this as unknown as WorkspaceSelectQueryBuilder, + objectMetadata, + internalContext: this.internalContext, + authContext: this.authContext, + featureFlagMap: this.featureFlagMap, + }); + } } diff --git a/packages/twenty-server/src/engine/twenty-orm/repository/workspace-update-query-builder.ts b/packages/twenty-server/src/engine/twenty-orm/repository/workspace-update-query-builder.ts index 3ccac8d6c5..9b1e335fb5 100644 --- a/packages/twenty-server/src/engine/twenty-orm/repository/workspace-update-query-builder.ts +++ b/packages/twenty-server/src/engine/twenty-orm/repository/workspace-update-query-builder.ts @@ -30,6 +30,7 @@ import { validateQueryIsPermittedOrThrow } from 'src/engine/twenty-orm/repositor import { type WorkspaceDeleteQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-delete-query-builder'; import { WorkspaceSelectQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-select-query-builder'; import { type WorkspaceSoftDeleteQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-soft-delete-query-builder'; +import { applyRowLevelPermissionPredicates } from 'src/engine/twenty-orm/utils/apply-row-level-permission-predicates.util'; import { applyTableAliasOnWhereCondition } from 'src/engine/twenty-orm/utils/apply-table-alias-on-where-condition'; import { computeEventSelectQueryBuilder } from 'src/engine/twenty-orm/utils/compute-event-select-query-builder.util'; import { formatData } from 'src/engine/twenty-orm/utils/format-data.util'; @@ -77,7 +78,7 @@ export class WorkspaceUpdateQueryBuilder< override clone(): this { const clonedQueryBuilder = super.clone(); - return new WorkspaceUpdateQueryBuilder( + const workspaceUpdateQueryBuilder = new WorkspaceUpdateQueryBuilder( clonedQueryBuilder as UpdateQueryBuilder, this.objectRecordsPermissions, this.internalContext, @@ -85,6 +86,8 @@ export class WorkspaceUpdateQueryBuilder< this.authContext, this.featureFlagMap, ) as this; + + return workspaceUpdateQueryBuilder; } override async execute(): Promise { @@ -92,7 +95,6 @@ export class WorkspaceUpdateQueryBuilder< if (this.manyInputs) { return this.executeMany(); } - validateQueryIsPermittedOrThrow({ expressionMap: this.expressionMap, objectsPermissions: this.objectRecordsPermissions, @@ -171,6 +173,8 @@ export class WorkspaceUpdateQueryBuilder< this.internalContext.flatFieldMetadataMaps, ); + this.applyRowLevelPermissionPredicates(); + const result = await super.execute(); const after = await eventSelectQueryBuilder.getMany(); @@ -323,6 +327,8 @@ export class WorkspaceUpdateQueryBuilder< this.expressionMap.valuesSet = input.partialEntity; this.where({ id: input.criteria }); + this.applyRowLevelPermissionPredicates(); + const result = await super.execute(); results.push(result); @@ -494,4 +500,25 @@ export class WorkspaceUpdateQueryBuilder< return this; } + + private applyRowLevelPermissionPredicates(): void { + if (this.shouldBypassPermissionChecks) { + return; + } + + const mainAliasTarget = this.getMainAliasTarget(); + + const objectMetadata = getObjectMetadataFromEntityTarget( + mainAliasTarget, + this.internalContext, + ); + + applyRowLevelPermissionPredicates({ + queryBuilder: this as unknown as WorkspaceSelectQueryBuilder, + objectMetadata, + internalContext: this.internalContext, + authContext: this.authContext, + featureFlagMap: this.featureFlagMap, + }); + } } diff --git a/packages/twenty-server/src/engine/twenty-orm/storage/orm-workspace-context.storage.ts b/packages/twenty-server/src/engine/twenty-orm/storage/orm-workspace-context.storage.ts index ec5170af16..b85f985e0a 100644 --- a/packages/twenty-server/src/engine/twenty-orm/storage/orm-workspace-context.storage.ts +++ b/packages/twenty-server/src/engine/twenty-orm/storage/orm-workspace-context.storage.ts @@ -10,6 +10,8 @@ import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/typ import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type'; import { type FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type'; import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type'; +import { type FlatRowLevelPermissionPredicateGroupMaps } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-group-maps.type'; +import { type FlatRowLevelPermissionPredicateMaps } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-maps.type'; export type ORMWorkspaceContext = { authContext: WorkspaceAuthContext; @@ -21,6 +23,8 @@ export type ORMWorkspaceContext = { permissionsPerRoleId: ObjectsPermissionsByRoleId; entityMetadatas: EntityMetadata[]; userWorkspaceRoleMap: Record; + flatRowLevelPermissionPredicateMaps: FlatRowLevelPermissionPredicateMaps; + flatRowLevelPermissionPredicateGroupMaps: FlatRowLevelPermissionPredicateGroupMaps; }; export const workspaceContextStorage = diff --git a/packages/twenty-server/src/engine/twenty-orm/utils/apply-row-level-permission-predicates.util.ts b/packages/twenty-server/src/engine/twenty-orm/utils/apply-row-level-permission-predicates.util.ts new file mode 100644 index 0000000000..21393ec1c4 --- /dev/null +++ b/packages/twenty-server/src/engine/twenty-orm/utils/apply-row-level-permission-predicates.util.ts @@ -0,0 +1,220 @@ +/* @license Enterprise */ + +import { + Brackets, + NotBrackets, + type ObjectLiteral, + type WhereExpressionBuilder, +} from 'typeorm'; + +import { type FeatureFlagMap } from 'src/engine/core-modules/feature-flag/interfaces/feature-flag-map.interface'; +import { type WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/workspace-internal-context.interface'; + +import { GraphqlQueryFilterFieldParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query-filter/graphql-query-filter-field.parser'; +import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type'; +import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum'; +import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type'; +import { type WorkspaceSelectQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-select-query-builder'; +import { buildRowLevelPermissionRecordFilter } from 'src/engine/twenty-orm/utils/build-row-level-permission-record-filter.util'; + +type ApplyRowLevelPermissionPredicatesArgs = { + queryBuilder: WorkspaceSelectQueryBuilder; + objectMetadata: FlatObjectMetadata; + internalContext: WorkspaceInternalContext; + authContext: AuthContext; + featureFlagMap: FeatureFlagMap; +}; + +export const applyRowLevelPermissionPredicates = ({ + queryBuilder, + objectMetadata, + internalContext, + authContext, + featureFlagMap, +}: ApplyRowLevelPermissionPredicatesArgs): void => { + if ( + featureFlagMap[ + FeatureFlagKey.IS_ROW_LEVEL_PERMISSION_PREDICATES_ENABLED + ] !== true + ) { + return; + } + + const roleId = authContext.userWorkspaceId + ? internalContext.userWorkspaceRoleMap[authContext.userWorkspaceId] + : undefined; + + const recordFilter = buildRowLevelPermissionRecordFilter({ + flatRowLevelPermissionPredicateMaps: + internalContext.flatRowLevelPermissionPredicateMaps, + flatRowLevelPermissionPredicateGroupMaps: + internalContext.flatRowLevelPermissionPredicateGroupMaps, + flatFieldMetadataMaps: internalContext.flatFieldMetadataMaps, + objectMetadata, + roleId, + authContext, + }); + + if (!recordFilter || Object.keys(recordFilter).length === 0) { + return; + } + + applyObjectRecordFilterToQueryBuilder({ + queryBuilder, + objectNameSingular: objectMetadata.nameSingular, + recordFilter, + fieldParser: new GraphqlQueryFilterFieldParser( + objectMetadata, + internalContext.flatFieldMetadataMaps, + ), + }); +}; + +const applyObjectRecordFilterToQueryBuilder = ({ + queryBuilder, + objectNameSingular, + recordFilter, + fieldParser, +}: { + queryBuilder: WorkspaceSelectQueryBuilder; + objectNameSingular: string; + recordFilter: Record; + fieldParser: GraphqlQueryFilterFieldParser; +}): void => { + if (!recordFilter || Object.keys(recordFilter).length === 0) { + return; + } + + const whereCondition = new Brackets((qb) => { + Object.entries(recordFilter).forEach(([key, value], index) => { + parseKeyFilter({ + queryBuilder: qb, + objectNameSingular, + key, + value, + isFirst: index === 0, + fieldParser, + }); + }); + }); + + if (queryBuilder.expressionMap.wheres.length === 0) { + queryBuilder.where(whereCondition); + } else { + queryBuilder.andWhere(whereCondition); + } +}; + +const parseKeyFilter = ({ + queryBuilder, + objectNameSingular, + key, + value, + isFirst, + fieldParser, +}: { + queryBuilder: WhereExpressionBuilder; + objectNameSingular: string; + key: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + value: any; + isFirst: boolean; + fieldParser: GraphqlQueryFilterFieldParser; +}): void => { + switch (key) { + case 'and': { + const andWhereCondition = new Brackets((qb) => { + value.forEach((filter: Record, index: number) => { + const whereCondition = new Brackets((qb2) => { + Object.entries(filter).forEach( + ([subFilterKey, subFilterValue], subIndex) => { + parseKeyFilter({ + queryBuilder: qb2, + objectNameSingular, + key: subFilterKey, + value: subFilterValue, + isFirst: subIndex === 0, + fieldParser, + }); + }, + ); + }); + + if (index === 0) { + qb.where(whereCondition); + } else { + qb.andWhere(whereCondition); + } + }); + }); + + if (isFirst) { + queryBuilder.where(andWhereCondition); + } else { + queryBuilder.andWhere(andWhereCondition); + } + break; + } + case 'or': { + const orWhereCondition = new Brackets((qb) => { + value.forEach((filter: Record, index: number) => { + const whereCondition = new Brackets((qb2) => { + Object.entries(filter).forEach( + ([subFilterKey, subFilterValue], subIndex) => { + parseKeyFilter({ + queryBuilder: qb2, + objectNameSingular, + key: subFilterKey, + value: subFilterValue, + isFirst: subIndex === 0, + fieldParser, + }); + }, + ); + }); + + if (index === 0) { + qb.where(whereCondition); + } else { + qb.orWhere(whereCondition); + } + }); + }); + + if (isFirst) { + queryBuilder.where(orWhereCondition); + } else { + queryBuilder.andWhere(orWhereCondition); + } + + break; + } + case 'not': { + const notWhereCondition = new NotBrackets((qb) => { + Object.entries(value).forEach( + ([subFilterKey, subFilterValue], subIndex) => { + parseKeyFilter({ + queryBuilder: qb, + objectNameSingular, + key: subFilterKey, + value: subFilterValue, + isFirst: subIndex === 0, + fieldParser, + }); + }, + ); + }); + + if (isFirst) { + queryBuilder.where(notWhereCondition); + } else { + queryBuilder.andWhere(notWhereCondition); + } + + break; + } + default: + fieldParser.parse(queryBuilder, objectNameSingular, key, value, isFirst); + break; + } +}; diff --git a/packages/twenty-server/src/engine/twenty-orm/utils/build-row-level-permission-record-filter.util.ts b/packages/twenty-server/src/engine/twenty-orm/utils/build-row-level-permission-record-filter.util.ts new file mode 100644 index 0000000000..47a382bf95 --- /dev/null +++ b/packages/twenty-server/src/engine/twenty-orm/utils/build-row-level-permission-record-filter.util.ts @@ -0,0 +1,201 @@ +/* @license Enterprise */ + +import { + RecordFilterGroupLogicalOperator, + type CompositeFieldSubFieldName, + type PartialFieldMetadataItemOption, + type RecordGqlOperationFilter, +} from 'twenty-shared/types'; +import { + computeRecordGqlOperationFilter, + convertViewFilterValueToString, + getFilterTypeFromFieldType, + isDefined, + type RecordFilter, + type RecordFilterGroup, +} from 'twenty-shared/utils'; + +import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type'; +import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util'; +import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type'; +import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type'; +import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type'; +import { + PermissionsException, + PermissionsExceptionCode, +} from 'src/engine/metadata-modules/permissions/permissions.exception'; +import { RowLevelPermissionPredicateGroupLogicalOperator } from 'src/engine/metadata-modules/row-level-permission-predicate/enums/row-level-permission-predicate-group-logical-operator.enum'; +import { type FlatRowLevelPermissionPredicateGroupMaps } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-group-maps.type'; +import { type FlatRowLevelPermissionPredicateMaps } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-maps.type'; +import { type RowLevelPermissionPredicateValue } from 'src/engine/metadata-modules/row-level-permission-predicate/types/row-level-permission-predicate-value.type'; + +type BuildRowLevelPermissionRecordFilterArgs = { + flatRowLevelPermissionPredicateMaps: FlatRowLevelPermissionPredicateMaps; + flatRowLevelPermissionPredicateGroupMaps: FlatRowLevelPermissionPredicateGroupMaps; + flatFieldMetadataMaps: FlatEntityMaps; + objectMetadata: FlatObjectMetadata; + roleId: string | undefined; + authContext: AuthContext; +}; + +export const buildRowLevelPermissionRecordFilter = ({ + flatRowLevelPermissionPredicateMaps, + flatRowLevelPermissionPredicateGroupMaps, + flatFieldMetadataMaps, + objectMetadata, + roleId, + authContext, +}: BuildRowLevelPermissionRecordFilterArgs): RecordGqlOperationFilter | null => { + if (!isDefined(roleId)) { + return null; + } + + const predicates = Object.values(flatRowLevelPermissionPredicateMaps.byId) + .filter(isDefined) + .filter( + (predicate) => + predicate.roleId === roleId && + predicate.objectMetadataId === objectMetadata.id && + !isDefined(predicate.deletedAt), + ); + + if (predicates.length === 0) { + return null; + } + + const fieldMetadataMapById = flatFieldMetadataMaps.byId; + + const workspaceMember = authContext.workspaceMember; + + const recordFilters = predicates + .map((predicate) => { + const fieldMetadata = fieldMetadataMapById[predicate.fieldMetadataId]; + + if (!isDefined(fieldMetadata)) { + throw new PermissionsException( + `Field metadata not found for row level predicate ${predicate.id}`, + PermissionsExceptionCode.FIELD_METADATA_NOT_FOUND, + ); + } + + const workspaceMemberFieldMetadataId = + predicate.workspaceMemberFieldMetadataId; + let predicateValue: RowLevelPermissionPredicateValue = predicate.value; + + if (isDefined(workspaceMemberFieldMetadataId)) { + const workspaceMemberFieldMetadata = + fieldMetadataMapById[workspaceMemberFieldMetadataId]; + + if (!isDefined(workspaceMemberFieldMetadata)) { + throw new PermissionsException( + `Workspace member field metadata not found for row level predicate ${predicate.id}`, + PermissionsExceptionCode.FIELD_METADATA_NOT_FOUND, + ); + } + + if (!isDefined(workspaceMember)) { + return null; + } + + const rawWorkspaceMemberValue = Object.entries(workspaceMember).find( + ([key]) => key === workspaceMemberFieldMetadata.name, + )?.[1]; + + const workspaceMemberSubFieldName = + predicate.workspaceMemberSubFieldName; + + if ( + isDefined(workspaceMemberSubFieldName) && + isDefined(rawWorkspaceMemberValue) && + isCompositeFieldMetadataType(workspaceMemberFieldMetadata.type) && + typeof rawWorkspaceMemberValue === 'object' + ) { + predicateValue = rawWorkspaceMemberValue[workspaceMemberSubFieldName]; + } else { + predicateValue = rawWorkspaceMemberValue; + } + + if (!isDefined(predicateValue)) { + throw new PermissionsException( + `Workspace member data missing for field ${workspaceMemberFieldMetadata.name}`, + PermissionsExceptionCode.INVALID_ARG, + ); + } + } + + const effectiveSubFieldName = predicate.subFieldName as + | CompositeFieldSubFieldName + | undefined; + + return { + id: predicate.id, + fieldMetadataId: predicate.fieldMetadataId, + value: convertViewFilterValueToString(predicateValue), + type: getFilterTypeFromFieldType(fieldMetadata.type), + operand: predicate.operand as unknown as RecordFilter['operand'], + recordFilterGroupId: predicate.rowLevelPermissionPredicateGroupId, + subFieldName: effectiveSubFieldName, + } satisfies RecordFilter; + }) + .filter(isDefined); + + const predicateGroupsById = flatRowLevelPermissionPredicateGroupMaps.byId; + + const relevantGroupIds = new Set(); + + for (const predicate of predicates) { + if (isDefined(predicate.rowLevelPermissionPredicateGroupId)) { + relevantGroupIds.add(predicate.rowLevelPermissionPredicateGroupId); + + let parentGroupId = + predicateGroupsById[predicate.rowLevelPermissionPredicateGroupId] + ?.parentRowLevelPermissionPredicateGroupId; + + while (isDefined(parentGroupId) && !relevantGroupIds.has(parentGroupId)) { + relevantGroupIds.add(parentGroupId); + parentGroupId = + predicateGroupsById[parentGroupId] + ?.parentRowLevelPermissionPredicateGroupId; + } + } + } + + const recordFilterGroups: RecordFilterGroup[] = [...relevantGroupIds] + .map((groupId) => predicateGroupsById[groupId]) + .filter(isDefined) + .filter( + (predicateGroup) => + predicateGroup.roleId === roleId && + !isDefined(predicateGroup.deletedAt), + ) + .map((predicateGroup) => ({ + id: predicateGroup.id, + logicalOperator: + predicateGroup.logicalOperator === + RowLevelPermissionPredicateGroupLogicalOperator.OR + ? RecordFilterGroupLogicalOperator.OR + : RecordFilterGroupLogicalOperator.AND, + parentRecordFilterGroupId: + predicateGroup.parentRowLevelPermissionPredicateGroupId, + })); + + const fieldMetadataItems = predicates + .map((predicate) => fieldMetadataMapById[predicate.fieldMetadataId]) + .filter(isDefined) + .map((field) => ({ + id: field.id, + name: field.name, + type: field.type, + label: field.label, + options: field.options as PartialFieldMetadataItemOption[], + })); + + return computeRecordGqlOperationFilter({ + recordFilters, + recordFilterGroups, + fields: fieldMetadataItems, + filterValueDependencies: { + currentWorkspaceMemberId: authContext.workspaceMemberId, + }, + }); +}; diff --git a/packages/twenty-server/src/engine/utils/bind-data-to-request-object.util.ts b/packages/twenty-server/src/engine/utils/bind-data-to-request-object.util.ts index 1b701da9ce..97b2aca40d 100644 --- a/packages/twenty-server/src/engine/utils/bind-data-to-request-object.util.ts +++ b/packages/twenty-server/src/engine/utils/bind-data-to-request-object.util.ts @@ -16,6 +16,7 @@ export const bindDataToRequestObject = ( request.workspaceId = data.workspace?.id; request.workspaceMetadataVersion = metadataVersion; request.workspaceMemberId = data.workspaceMemberId; + request.workspaceMember = data.workspaceMember; request.userWorkspaceId = data.userWorkspaceId; request.authProvider = data.authProvider; request.impersonationContext = data.impersonationContext; 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 518d35922a..13d684c682 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 @@ -6,6 +6,8 @@ import { type FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/ import { type FlatRoleTargetByAgentIdMaps } from 'src/engine/metadata-modules/flat-agent/types/flat-role-target-by-agent-id-maps.type'; import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type'; import { type UserWorkspaceRoleMap } from 'src/engine/metadata-modules/role-target/services/workspace-user-workspace-role-map-cache.service'; +import { type FlatRowLevelPermissionPredicateGroupMaps } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-group-maps.type'; +import { type FlatRowLevelPermissionPredicateMaps } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-maps.type'; export const WORKSPACE_CACHE_KEYS_V2 = { flatObjectMetadataMaps: 'flat-maps:object-metadata', @@ -32,6 +34,10 @@ export const WORKSPACE_CACHE_KEYS_V2 = { flatPageLayoutMaps: 'flat-maps:page-layout', flatPageLayoutWidgetMaps: 'flat-maps:page-layout-widget', flatPageLayoutTabMaps: 'flat-maps:flatPageLayoutTabMaps', + flatRowLevelPermissionPredicateMaps: + 'flat-maps:row-level-permission-predicate', + flatRowLevelPermissionPredicateGroupMaps: + 'flat-maps:row-level-permission-predicate-group', } as const satisfies Record; export type AdditionalCacheDataMaps = { @@ -42,6 +48,8 @@ export type AdditionalCacheDataMaps = { flatApplicationMaps: FlatApplicationCacheMaps; ORMEntityMetadatas: EntityMetadata[]; flatRoleTargetByAgentIdMaps: FlatRoleTargetByAgentIdMaps; + flatRowLevelPermissionPredicateMaps: FlatRowLevelPermissionPredicateMaps; + flatRowLevelPermissionPredicateGroupMaps: FlatRowLevelPermissionPredicateGroupMaps; }; export type WorkspaceCacheDataMap = AllFlatEntityMaps & AdditionalCacheDataMaps; 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 5b6404b2a0..2b23e57901 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 @@ -35,6 +35,8 @@ export const fromWorkspaceMigrationBuilderExceptionToMetadataValidationResponseE invalidPageLayoutWidget: 0, totalErrors: 0, invalidPageLayoutTab: 0, + invalidRowLevelPermissionPredicate: 0, + invalidRowLevelPermissionPredicateGroup: 0, }, errors: translatedReport, }; 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 2396441f01..7a7b15332a 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 @@ -26,6 +26,8 @@ import { WorkspaceMigrationV2PageLayoutActionsBuilderService } from 'src/engine/ import { WorkspaceMigrationV2RoleTargetActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/role-target/workspace-migration-v2-role-target-actions-builder.service'; import { WorkspaceMigrationV2RoleActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/role/workspace-migration-v2-role-actions-builder.service'; import { WorkspaceMigrationV2RouteTriggerActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/route-trigger/workspace-migration-v2-route-trigger-actions-builder.service'; +import { WorkspaceMigrationV2RowLevelPermissionPredicateGroupActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/row-level-permission-predicate-group/workspace-migration-v2-row-level-permission-predicate-group-actions-builder.service'; +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 { WorkspaceMigrationV2ViewFilterActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view-filter/workspace-migration-v2-view-filter-actions-builder.service'; @@ -52,6 +54,8 @@ export class WorkspaceMigrationBuildOrchestratorService { private readonly workspaceMigrationV2PageLayoutActionsBuilderService: WorkspaceMigrationV2PageLayoutActionsBuilderService, private readonly workspaceMigrationV2PageLayoutWidgetActionsBuilderService: WorkspaceMigrationV2PageLayoutWidgetActionsBuilderService, private readonly workspaceMigrationV2PageLayoutTabActionsBuilderService: WorkspaceMigrationV2PageLayoutTabActionsBuilderService, + private readonly workspaceMigrationV2RowLevelPermissionPredicateActionsBuilderService: WorkspaceMigrationV2RowLevelPermissionPredicateActionsBuilderService, + private readonly workspaceMigrationV2RowLevelPermissionPredicateGroupActionsBuilderService: WorkspaceMigrationV2RowLevelPermissionPredicateGroupActionsBuilderService, ) {} private setupOptimisticCache({ @@ -141,6 +145,8 @@ export class WorkspaceMigrationBuildOrchestratorService { flatFieldMetadataMaps, flatViewFilterMaps, flatViewGroupMaps, + flatRowLevelPermissionPredicateMaps, + flatRowLevelPermissionPredicateGroupMaps, flatRoleMaps, flatRoleTargetMaps, flatAgentMaps, @@ -409,6 +415,86 @@ export class WorkspaceMigrationBuildOrchestratorService { } } + if (isDefined(flatRowLevelPermissionPredicateGroupMaps)) { + const { + from: fromFlatRowLevelPermissionPredicateGroupMaps, + to: toFlatRowLevelPermissionPredicateGroupMaps, + } = flatRowLevelPermissionPredicateGroupMaps; + const rowLevelPermissionPredicateGroupResult = + await this.workspaceMigrationV2RowLevelPermissionPredicateGroupActionsBuilderService.validateAndBuild( + { + additionalCacheDataMaps, + from: fromFlatRowLevelPermissionPredicateGroupMaps, + to: toFlatRowLevelPermissionPredicateGroupMaps, + buildOptions, + dependencyOptimisticFlatEntityMaps: { + flatRoleMaps: optimisticAllFlatEntityMaps.flatRoleMaps, + }, + workspaceId, + }, + ); + + this.mergeFlatEntityMapsAndRelatedFlatEntityMapsInAllFlatEntityMapsThroughMutation( + { + allFlatEntityMaps: optimisticAllFlatEntityMaps, + flatEntityMapsAndRelatedFlatEntityMaps: + rowLevelPermissionPredicateGroupResult.optimisticFlatEntityMapsAndRelatedFlatEntityMaps, + }, + ); + + if (rowLevelPermissionPredicateGroupResult.status === 'fail') { + orchestratorFailureReport.rowLevelPermissionPredicateGroup.push( + ...rowLevelPermissionPredicateGroupResult.errors, + ); + } else { + orchestratorActionsReport.rowLevelPermissionPredicateGroup = + rowLevelPermissionPredicateGroupResult.actions; + } + } + + if (isDefined(flatRowLevelPermissionPredicateMaps)) { + const { + from: fromFlatRowLevelPermissionPredicateMaps, + to: toFlatRowLevelPermissionPredicateMaps, + } = flatRowLevelPermissionPredicateMaps; + const rowLevelPermissionPredicateResult = + await this.workspaceMigrationV2RowLevelPermissionPredicateActionsBuilderService.validateAndBuild( + { + additionalCacheDataMaps, + from: fromFlatRowLevelPermissionPredicateMaps, + to: toFlatRowLevelPermissionPredicateMaps, + buildOptions, + dependencyOptimisticFlatEntityMaps: { + flatFieldMetadataMaps: + optimisticAllFlatEntityMaps.flatFieldMetadataMaps, + flatObjectMetadataMaps: + optimisticAllFlatEntityMaps.flatObjectMetadataMaps, + flatRowLevelPermissionPredicateGroupMaps: + optimisticAllFlatEntityMaps.flatRowLevelPermissionPredicateGroupMaps, + flatRoleMaps: optimisticAllFlatEntityMaps.flatRoleMaps, + }, + workspaceId, + }, + ); + + this.mergeFlatEntityMapsAndRelatedFlatEntityMapsInAllFlatEntityMapsThroughMutation( + { + allFlatEntityMaps: optimisticAllFlatEntityMaps, + flatEntityMapsAndRelatedFlatEntityMaps: + rowLevelPermissionPredicateResult.optimisticFlatEntityMapsAndRelatedFlatEntityMaps, + }, + ); + + if (rowLevelPermissionPredicateResult.status === 'fail') { + orchestratorFailureReport.rowLevelPermissionPredicate.push( + ...rowLevelPermissionPredicateResult.errors, + ); + } else { + orchestratorActionsReport.rowLevelPermissionPredicate = + rowLevelPermissionPredicateResult.actions; + } + } + if (isDefined(flatServerlessFunctionMaps)) { const { from: fromFlatServerlessFunctionMaps, @@ -871,6 +957,24 @@ export class WorkspaceMigrationBuildOrchestratorService { ...aggregatedOrchestratorActionsReport.pageLayoutWidget.created, ...aggregatedOrchestratorActionsReport.pageLayoutWidget.updated, /// + + // Row level permission predicates + ...aggregatedOrchestratorActionsReport.rowLevelPermissionPredicate + .deleted, + ...aggregatedOrchestratorActionsReport.rowLevelPermissionPredicate + .created, + ...aggregatedOrchestratorActionsReport.rowLevelPermissionPredicate + .updated, + /// + + // Row level permission predicate groups + ...aggregatedOrchestratorActionsReport + .rowLevelPermissionPredicateGroup.deleted, + ...aggregatedOrchestratorActionsReport + .rowLevelPermissionPredicateGroup.created, + ...aggregatedOrchestratorActionsReport + .rowLevelPermissionPredicateGroup.updated, + /// ], workspaceId, }, diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/row-level-permission-predicate-group/types/workspace-migration-row-level-permission-predicate-group-action-v2.type.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/row-level-permission-predicate-group/types/workspace-migration-row-level-permission-predicate-group-action-v2.type.ts new file mode 100644 index 0000000000..67f68bc388 --- /dev/null +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/row-level-permission-predicate-group/types/workspace-migration-row-level-permission-predicate-group-action-v2.type.ts @@ -0,0 +1,28 @@ +/* @license Enterprise */ + +import { type FlatEntityPropertiesUpdates } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-properties-updates.type'; +import { type FlatRowLevelPermissionPredicateGroup } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-group.type'; + +export type CreateRowLevelPermissionPredicateGroupAction = { + type: 'create_row_level_permission_predicate_group'; + rowLevelPermissionPredicateGroup: FlatRowLevelPermissionPredicateGroup; +}; + +export type UpdateRowLevelPermissionPredicateGroupAction = { + type: 'update_row_level_permission_predicate_group'; + rowLevelPermissionPredicateGroupId: string; + updates: FlatEntityPropertiesUpdates<'rowLevelPermissionPredicateGroup'>; +}; + +export type DeleteRowLevelPermissionPredicateGroupAction = { + type: 'delete_row_level_permission_predicate_group'; + rowLevelPermissionPredicateGroupId: string; +}; + +export type WorkspaceMigrationRowLevelPermissionPredicateGroupActionV2 = + | CreateRowLevelPermissionPredicateGroupAction + | UpdateRowLevelPermissionPredicateGroupAction + | DeleteRowLevelPermissionPredicateGroupAction; + +export type WorkspaceMigrationRowLevelPermissionPredicateGroupActionTypeV2 = + WorkspaceMigrationRowLevelPermissionPredicateGroupActionV2['type']; diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/row-level-permission-predicate-group/workspace-migration-v2-row-level-permission-predicate-group-actions-builder.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/row-level-permission-predicate-group/workspace-migration-v2-row-level-permission-predicate-group-actions-builder.service.ts new file mode 100644 index 0000000000..94c1b38464 --- /dev/null +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/row-level-permission-predicate-group/workspace-migration-v2-row-level-permission-predicate-group-actions-builder.service.ts @@ -0,0 +1,121 @@ +/* @license Enterprise */ + +import { Injectable } from '@nestjs/common'; + +import { ALL_METADATA_NAME } from 'twenty-shared/metadata'; + +import { UpdateRowLevelPermissionPredicateGroupAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/row-level-permission-predicate-group/types/workspace-migration-row-level-permission-predicate-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 { 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 { FlatEntityValidationReturnType } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/flat-entity-validation-result.type'; +import { FlatRowLevelPermissionPredicateGroupValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-row-level-permission-predicate-group-validator.service'; + +@Injectable() +export class WorkspaceMigrationV2RowLevelPermissionPredicateGroupActionsBuilderService extends WorkspaceEntityMigrationBuilderV2Service< + typeof ALL_METADATA_NAME.rowLevelPermissionPredicateGroup +> { + constructor( + private readonly flatRowLevelPermissionPredicateGroupValidatorService: FlatRowLevelPermissionPredicateGroupValidatorService, + ) { + super(ALL_METADATA_NAME.rowLevelPermissionPredicateGroup); + } + + protected validateFlatEntityCreation( + args: FlatEntityValidationArgs< + typeof ALL_METADATA_NAME.rowLevelPermissionPredicateGroup + >, + ): FlatEntityValidationReturnType< + typeof ALL_METADATA_NAME.rowLevelPermissionPredicateGroup, + 'created' + > { + const validationResult = + this.flatRowLevelPermissionPredicateGroupValidatorService.validateFlatRowLevelPermissionPredicateGroupCreation( + args, + ); + + if (validationResult.errors.length > 0) { + return { + status: 'fail', + ...validationResult, + }; + } + + const { flatEntityToValidate } = args; + + return { + status: 'success', + action: { + type: 'create_row_level_permission_predicate_group', + rowLevelPermissionPredicateGroup: flatEntityToValidate, + }, + }; + } + + protected validateFlatEntityDeletion( + args: FlatEntityValidationArgs< + typeof ALL_METADATA_NAME.rowLevelPermissionPredicateGroup + >, + ): FlatEntityValidationReturnType< + typeof ALL_METADATA_NAME.rowLevelPermissionPredicateGroup, + 'deleted' + > { + const validationResult = + this.flatRowLevelPermissionPredicateGroupValidatorService.validateFlatRowLevelPermissionPredicateGroupDeletion( + args, + ); + + if (validationResult.errors.length > 0) { + return { + status: 'fail', + ...validationResult, + }; + } + + const { + flatEntityToValidate: { id: predicateGroupId }, + } = args; + + return { + status: 'success', + action: { + type: 'delete_row_level_permission_predicate_group', + rowLevelPermissionPredicateGroupId: predicateGroupId, + }, + }; + } + + protected validateFlatEntityUpdate( + args: FlatEntityUpdateValidationArgs< + typeof ALL_METADATA_NAME.rowLevelPermissionPredicateGroup + >, + ): FlatEntityValidationReturnType< + typeof ALL_METADATA_NAME.rowLevelPermissionPredicateGroup, + 'updated' + > { + const validationResult = + this.flatRowLevelPermissionPredicateGroupValidatorService.validateFlatRowLevelPermissionPredicateGroupUpdate( + args, + ); + + if (validationResult.errors.length > 0) { + return { + status: 'fail', + ...validationResult, + }; + } + + const { flatEntityId, flatEntityUpdates } = args; + + const updateAction: UpdateRowLevelPermissionPredicateGroupAction = { + type: 'update_row_level_permission_predicate_group', + rowLevelPermissionPredicateGroupId: flatEntityId, + updates: flatEntityUpdates, + }; + + return { + status: 'success', + action: updateAction, + }; + } +} diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/row-level-permission-predicate/types/workspace-migration-row-level-permission-predicate-action-v2.type.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/row-level-permission-predicate/types/workspace-migration-row-level-permission-predicate-action-v2.type.ts new file mode 100644 index 0000000000..73aaa0bb28 --- /dev/null +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/row-level-permission-predicate/types/workspace-migration-row-level-permission-predicate-action-v2.type.ts @@ -0,0 +1,34 @@ +/* @license Enterprise */ + +import { type FlatEntityPropertiesUpdates } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-properties-updates.type'; +import { type FlatRowLevelPermissionPredicate } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate.type'; + +export type CreateRowLevelPermissionPredicateAction = { + type: 'create_row_level_permission_predicate'; + rowLevelPermissionPredicate: FlatRowLevelPermissionPredicate; +}; + +export type UpdateRowLevelPermissionPredicateAction = { + type: 'update_row_level_permission_predicate'; + rowLevelPermissionPredicateId: string; + updates: FlatEntityPropertiesUpdates<'rowLevelPermissionPredicate'>; +}; + +export type DeleteRowLevelPermissionPredicateAction = { + type: 'delete_row_level_permission_predicate'; + rowLevelPermissionPredicateId: string; +}; + +export type DestroyRowLevelPermissionPredicateAction = { + type: 'destroy_row_level_permission_predicate'; + rowLevelPermissionPredicateId: string; +}; + +export type WorkspaceMigrationRowLevelPermissionPredicateActionV2 = + | CreateRowLevelPermissionPredicateAction + | UpdateRowLevelPermissionPredicateAction + | DeleteRowLevelPermissionPredicateAction + | DestroyRowLevelPermissionPredicateAction; + +export type WorkspaceMigrationRowLevelPermissionPredicateActionTypeV2 = + WorkspaceMigrationRowLevelPermissionPredicateActionV2['type']; diff --git a/packages/twenty-server/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.ts b/packages/twenty-server/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.ts new file mode 100644 index 0000000000..54b673a933 --- /dev/null +++ b/packages/twenty-server/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.ts @@ -0,0 +1,121 @@ +/* @license Enterprise */ + +import { Injectable } from '@nestjs/common'; + +import { ALL_METADATA_NAME } from 'twenty-shared/metadata'; + +import { UpdateRowLevelPermissionPredicateAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/row-level-permission-predicate/types/workspace-migration-row-level-permission-predicate-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 { 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 { FlatEntityValidationReturnType } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/flat-entity-validation-result.type'; +import { FlatRowLevelPermissionPredicateValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-row-level-permission-predicate-validator.service'; + +@Injectable() +export class WorkspaceMigrationV2RowLevelPermissionPredicateActionsBuilderService extends WorkspaceEntityMigrationBuilderV2Service< + typeof ALL_METADATA_NAME.rowLevelPermissionPredicate +> { + constructor( + private readonly flatRowLevelPermissionPredicateValidatorService: FlatRowLevelPermissionPredicateValidatorService, + ) { + super(ALL_METADATA_NAME.rowLevelPermissionPredicate); + } + + protected validateFlatEntityCreation( + args: FlatEntityValidationArgs< + typeof ALL_METADATA_NAME.rowLevelPermissionPredicate + >, + ): FlatEntityValidationReturnType< + typeof ALL_METADATA_NAME.rowLevelPermissionPredicate, + 'created' + > { + const validationResult = + this.flatRowLevelPermissionPredicateValidatorService.validateFlatRowLevelPermissionPredicateCreation( + args, + ); + + if (validationResult.errors.length > 0) { + return { + status: 'fail', + ...validationResult, + }; + } + + const { flatEntityToValidate } = args; + + return { + status: 'success', + action: { + type: 'create_row_level_permission_predicate', + rowLevelPermissionPredicate: flatEntityToValidate, + }, + }; + } + + protected validateFlatEntityDeletion( + args: FlatEntityValidationArgs< + typeof ALL_METADATA_NAME.rowLevelPermissionPredicate + >, + ): FlatEntityValidationReturnType< + typeof ALL_METADATA_NAME.rowLevelPermissionPredicate, + 'deleted' + > { + const validationResult = + this.flatRowLevelPermissionPredicateValidatorService.validateFlatRowLevelPermissionPredicateDeletion( + args, + ); + + if (validationResult.errors.length > 0) { + return { + status: 'fail', + ...validationResult, + }; + } + + const { + flatEntityToValidate: { id: predicateId }, + } = args; + + return { + status: 'success', + action: { + type: 'delete_row_level_permission_predicate', + rowLevelPermissionPredicateId: predicateId, + }, + }; + } + + protected validateFlatEntityUpdate( + args: FlatEntityUpdateValidationArgs< + typeof ALL_METADATA_NAME.rowLevelPermissionPredicate + >, + ): FlatEntityValidationReturnType< + typeof ALL_METADATA_NAME.rowLevelPermissionPredicate, + 'updated' + > { + const validationResult = + this.flatRowLevelPermissionPredicateValidatorService.validateFlatRowLevelPermissionPredicateUpdate( + args, + ); + + if (validationResult.errors.length > 0) { + return { + status: 'fail', + ...validationResult, + }; + } + + const { flatEntityId, flatEntityUpdates } = args; + + const updateAction: UpdateRowLevelPermissionPredicateAction = { + type: 'update_row_level_permission_predicate', + rowLevelPermissionPredicateId: flatEntityId, + updates: flatEntityUpdates, + }; + + return { + status: 'success', + action: updateAction, + }; + } +} diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-row-level-permission-predicate-group-validator.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-row-level-permission-predicate-group-validator.service.ts new file mode 100644 index 0000000000..940f659171 --- /dev/null +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-row-level-permission-predicate-group-validator.service.ts @@ -0,0 +1,209 @@ +/* @license Enterprise */ + +import { Injectable } from '@nestjs/common'; + +import { msg, t } from '@lingui/core/macro'; +import { 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 { RowLevelPermissionPredicateGroupExceptionCode } from 'src/engine/metadata-modules/row-level-permission-predicate/exceptions/row-level-permission-predicate-group.exception'; +import { type FlatRowLevelPermissionPredicateGroup } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-group.type'; +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 { 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 FlatRowLevelPermissionPredicateGroupValidatorService { + validateFlatRowLevelPermissionPredicateGroupCreation({ + flatEntityToValidate: flatPredicateGroupToValidate, + optimisticFlatEntityMapsAndRelatedFlatEntityMaps, + }: FlatEntityValidationArgs< + typeof ALL_METADATA_NAME.rowLevelPermissionPredicateGroup + >): FailedFlatEntityValidation { + const { + flatRowLevelPermissionPredicateGroupMaps: + optimisticFlatPredicateGroupMaps, + flatRoleMaps, + } = optimisticFlatEntityMapsAndRelatedFlatEntityMaps as Partial<{ + flatRowLevelPermissionPredicateGroupMaps: typeof optimisticFlatEntityMapsAndRelatedFlatEntityMaps.flatRowLevelPermissionPredicateGroupMaps; + flatRoleMaps: typeof optimisticFlatEntityMapsAndRelatedFlatEntityMaps.flatRoleMaps; + }>; + const validationResult: FailedFlatEntityValidation = + { + type: 'create_row_level_permission_predicate_group', + errors: [], + flatEntityMinimalInformation: { + id: flatPredicateGroupToValidate.id, + }, + }; + + const existingPredicateGroup = + optimisticFlatPredicateGroupMaps?.byId[flatPredicateGroupToValidate.id]; + + if (isDefined(existingPredicateGroup)) { + validationResult.errors.push({ + code: RowLevelPermissionPredicateGroupExceptionCode.INVALID_ROW_LEVEL_PERMISSION_PREDICATE_GROUP_DATA, + message: t`Row level permission predicate group with this id already exists`, + userFriendlyMessage: msg`Row level permission predicate group with this id already exists`, + }); + } + + if ( + isDefined( + flatPredicateGroupToValidate.parentRowLevelPermissionPredicateGroupId, + ) && + optimisticFlatPredicateGroupMaps + ) { + const parentGroup = findFlatEntityByIdInFlatEntityMaps({ + flatEntityId: + flatPredicateGroupToValidate.parentRowLevelPermissionPredicateGroupId, + flatEntityMaps: optimisticFlatPredicateGroupMaps, + }); + + if (!isDefined(parentGroup)) { + validationResult.errors.push({ + code: RowLevelPermissionPredicateGroupExceptionCode.INVALID_ROW_LEVEL_PERMISSION_PREDICATE_GROUP_DATA, + message: t`Parent row level permission predicate group not found`, + userFriendlyMessage: msg`Parent row level permission predicate group not found`, + }); + } + } + + const role = flatRoleMaps + ? findFlatEntityByIdInFlatEntityMaps({ + flatEntityId: flatPredicateGroupToValidate.roleId, + flatEntityMaps: flatRoleMaps, + }) + : undefined; + + if (!isDefined(role)) { + validationResult.errors.push({ + code: RowLevelPermissionPredicateGroupExceptionCode.ROLE_NOT_FOUND, + message: t`Role not found`, + userFriendlyMessage: msg`Role not found`, + }); + } + + return validationResult; + } + + validateFlatRowLevelPermissionPredicateGroupDeletion({ + flatEntityToValidate: flatPredicateGroupToDelete, + optimisticFlatEntityMapsAndRelatedFlatEntityMaps, + }: FlatEntityValidationArgs< + typeof ALL_METADATA_NAME.rowLevelPermissionPredicateGroup + >): FailedFlatEntityValidation { + const { + flatRowLevelPermissionPredicateGroupMaps: + optimisticFlatPredicateGroupMaps, + } = optimisticFlatEntityMapsAndRelatedFlatEntityMaps as Partial<{ + flatRowLevelPermissionPredicateGroupMaps: typeof optimisticFlatEntityMapsAndRelatedFlatEntityMaps.flatRowLevelPermissionPredicateGroupMaps; + }>; + const validationResult: FailedFlatEntityValidation = + { + type: 'delete_row_level_permission_predicate_group', + errors: [], + flatEntityMinimalInformation: { + id: flatPredicateGroupToDelete.id, + }, + }; + + const existingPredicateGroup = + optimisticFlatPredicateGroupMaps?.byId[flatPredicateGroupToDelete.id]; + + if (!isDefined(existingPredicateGroup)) { + validationResult.errors.push({ + code: RowLevelPermissionPredicateGroupExceptionCode.ROW_LEVEL_PERMISSION_PREDICATE_GROUP_NOT_FOUND, + message: t`Row level permission predicate group to delete not found`, + userFriendlyMessage: msg`Row level permission predicate group to delete not found`, + }); + } + + return validationResult; + } + + validateFlatRowLevelPermissionPredicateGroupUpdate({ + flatEntityId, + flatEntityUpdates, + optimisticFlatEntityMapsAndRelatedFlatEntityMaps, + }: FlatEntityUpdateValidationArgs< + typeof ALL_METADATA_NAME.rowLevelPermissionPredicateGroup + >): FailedFlatEntityValidation { + const { + flatRowLevelPermissionPredicateGroupMaps: + optimisticFlatPredicateGroupMaps, + flatRoleMaps, + } = optimisticFlatEntityMapsAndRelatedFlatEntityMaps as Partial<{ + flatRowLevelPermissionPredicateGroupMaps: typeof optimisticFlatEntityMapsAndRelatedFlatEntityMaps.flatRowLevelPermissionPredicateGroupMaps; + flatRoleMaps: typeof optimisticFlatEntityMapsAndRelatedFlatEntityMaps.flatRoleMaps; + }>; + const validationResult: FailedFlatEntityValidation = + { + type: 'update_row_level_permission_predicate_group', + errors: [], + flatEntityMinimalInformation: { + id: flatEntityId, + }, + }; + + const existingPredicateGroup = + optimisticFlatPredicateGroupMaps?.byId[flatEntityId]; + + if (!isDefined(existingPredicateGroup)) { + validationResult.errors.push({ + code: RowLevelPermissionPredicateGroupExceptionCode.ROW_LEVEL_PERMISSION_PREDICATE_GROUP_NOT_FOUND, + message: t`Row level permission predicate group to update not found`, + userFriendlyMessage: msg`Row level permission predicate group to update not found`, + }); + + return validationResult; + } + + const updatedPredicateGroup = { + ...existingPredicateGroup, + ...fromFlatEntityPropertiesUpdatesToPartialFlatEntity({ + updates: flatEntityUpdates, + }), + }; + + if ( + isDefined( + updatedPredicateGroup.parentRowLevelPermissionPredicateGroupId, + ) && + optimisticFlatPredicateGroupMaps + ) { + const parentGroup = findFlatEntityByIdInFlatEntityMaps({ + flatEntityId: + updatedPredicateGroup.parentRowLevelPermissionPredicateGroupId, + flatEntityMaps: optimisticFlatPredicateGroupMaps, + }); + + if (!isDefined(parentGroup)) { + validationResult.errors.push({ + code: RowLevelPermissionPredicateGroupExceptionCode.INVALID_ROW_LEVEL_PERMISSION_PREDICATE_GROUP_DATA, + message: t`Parent row level permission predicate group not found`, + userFriendlyMessage: msg`Parent row level permission predicate group not found`, + }); + } + } + + const role = flatRoleMaps + ? findFlatEntityByIdInFlatEntityMaps({ + flatEntityId: updatedPredicateGroup.roleId, + flatEntityMaps: flatRoleMaps, + }) + : undefined; + + if (!isDefined(role)) { + validationResult.errors.push({ + code: RowLevelPermissionPredicateGroupExceptionCode.ROLE_NOT_FOUND, + message: t`Role not found`, + userFriendlyMessage: msg`Role not found`, + }); + } + + return validationResult; + } +} diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-row-level-permission-predicate-validator.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-row-level-permission-predicate-validator.service.ts new file mode 100644 index 0000000000..ea514f4dde --- /dev/null +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-row-level-permission-predicate-validator.service.ts @@ -0,0 +1,272 @@ +/* @license Enterprise */ + +import { Injectable } from '@nestjs/common'; + +import { msg, t } from '@lingui/core/macro'; +import { 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 { RowLevelPermissionPredicateExceptionCode } from 'src/engine/metadata-modules/row-level-permission-predicate/exceptions/row-level-permission-predicate.exception'; +import { type FlatRowLevelPermissionPredicate } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate.type'; +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 { 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 FlatRowLevelPermissionPredicateValidatorService { + validateFlatRowLevelPermissionPredicateCreation({ + flatEntityToValidate: flatPredicateToValidate, + optimisticFlatEntityMapsAndRelatedFlatEntityMaps, + }: FlatEntityValidationArgs< + typeof ALL_METADATA_NAME.rowLevelPermissionPredicate + >): FailedFlatEntityValidation { + const { + flatRowLevelPermissionPredicateMaps: optimisticFlatPredicateMaps, + flatFieldMetadataMaps, + flatObjectMetadataMaps, + flatRowLevelPermissionPredicateGroupMaps, + flatRoleMaps, + } = optimisticFlatEntityMapsAndRelatedFlatEntityMaps as Partial<{ + flatRowLevelPermissionPredicateMaps: typeof optimisticFlatEntityMapsAndRelatedFlatEntityMaps.flatRowLevelPermissionPredicateMaps; + flatFieldMetadataMaps: typeof optimisticFlatEntityMapsAndRelatedFlatEntityMaps.flatFieldMetadataMaps; + flatObjectMetadataMaps: typeof optimisticFlatEntityMapsAndRelatedFlatEntityMaps.flatObjectMetadataMaps; + flatRowLevelPermissionPredicateGroupMaps: typeof optimisticFlatEntityMapsAndRelatedFlatEntityMaps.flatRowLevelPermissionPredicateGroupMaps; + flatRoleMaps: typeof optimisticFlatEntityMapsAndRelatedFlatEntityMaps.flatRoleMaps; + }>; + + const validationResult: FailedFlatEntityValidation = + { + type: 'create_row_level_permission_predicate', + errors: [], + flatEntityMinimalInformation: { + id: flatPredicateToValidate.id, + }, + }; + + const existingPredicate = + optimisticFlatPredicateMaps?.byId[flatPredicateToValidate.id]; + + if (isDefined(existingPredicate)) { + validationResult.errors.push({ + code: RowLevelPermissionPredicateExceptionCode.INVALID_ROW_LEVEL_PERMISSION_PREDICATE_DATA, + message: t`Row level permission predicate with this id already exists`, + userFriendlyMessage: msg`Row level permission predicate with this id already exists`, + }); + } + + const fieldMetadata = flatFieldMetadataMaps + ? findFlatEntityByIdInFlatEntityMaps({ + flatEntityId: flatPredicateToValidate.fieldMetadataId, + flatEntityMaps: flatFieldMetadataMaps, + }) + : undefined; + + if (!isDefined(fieldMetadata)) { + validationResult.errors.push({ + code: RowLevelPermissionPredicateExceptionCode.FIELD_METADATA_NOT_FOUND, + message: t`Field metadata not found`, + userFriendlyMessage: msg`Field metadata not found`, + }); + } + + const objectMetadata = flatObjectMetadataMaps + ? findFlatEntityByIdInFlatEntityMaps({ + flatEntityId: flatPredicateToValidate.objectMetadataId, + flatEntityMaps: flatObjectMetadataMaps, + }) + : undefined; + + if (!isDefined(objectMetadata)) { + validationResult.errors.push({ + code: RowLevelPermissionPredicateExceptionCode.OBJECT_METADATA_NOT_FOUND, + message: t`Object metadata not found`, + userFriendlyMessage: msg`Object metadata not found`, + }); + } + + if ( + isDefined(flatPredicateToValidate.rowLevelPermissionPredicateGroupId) && + flatRowLevelPermissionPredicateGroupMaps + ) { + const predicateGroup = findFlatEntityByIdInFlatEntityMaps({ + flatEntityId: + flatPredicateToValidate.rowLevelPermissionPredicateGroupId, + flatEntityMaps: flatRowLevelPermissionPredicateGroupMaps, + }); + + if (!isDefined(predicateGroup)) { + validationResult.errors.push({ + code: RowLevelPermissionPredicateExceptionCode.INVALID_ROW_LEVEL_PERMISSION_PREDICATE_DATA, + message: t`Row level permission predicate group not found`, + userFriendlyMessage: msg`Row level permission predicate group not found`, + }); + } + } + + const role = flatRoleMaps + ? findFlatEntityByIdInFlatEntityMaps({ + flatEntityId: flatPredicateToValidate.roleId, + flatEntityMaps: flatRoleMaps, + }) + : undefined; + + if (!isDefined(role)) { + validationResult.errors.push({ + code: RowLevelPermissionPredicateExceptionCode.ROLE_NOT_FOUND, + message: t`Role not found`, + userFriendlyMessage: msg`Role not found`, + }); + } + + return validationResult; + } + + validateFlatRowLevelPermissionPredicateDeletion({ + flatEntityToValidate: flatPredicateToDelete, + optimisticFlatEntityMapsAndRelatedFlatEntityMaps, + }: FlatEntityValidationArgs< + typeof ALL_METADATA_NAME.rowLevelPermissionPredicate + >): FailedFlatEntityValidation { + const { flatRowLevelPermissionPredicateMaps: optimisticFlatPredicateMaps } = + optimisticFlatEntityMapsAndRelatedFlatEntityMaps as Partial<{ + flatRowLevelPermissionPredicateMaps: typeof optimisticFlatEntityMapsAndRelatedFlatEntityMaps.flatRowLevelPermissionPredicateMaps; + }>; + const validationResult: FailedFlatEntityValidation = + { + type: 'delete_row_level_permission_predicate', + errors: [], + flatEntityMinimalInformation: { + id: flatPredicateToDelete.id, + }, + }; + + const existingPredicate = + optimisticFlatPredicateMaps?.byId[flatPredicateToDelete.id]; + + if (!isDefined(existingPredicate)) { + validationResult.errors.push({ + code: RowLevelPermissionPredicateExceptionCode.ROW_LEVEL_PERMISSION_PREDICATE_NOT_FOUND, + message: t`Row level permission predicate to delete not found`, + userFriendlyMessage: msg`Row level permission predicate to delete not found`, + }); + } + + return validationResult; + } + + validateFlatRowLevelPermissionPredicateUpdate({ + flatEntityId, + flatEntityUpdates, + optimisticFlatEntityMapsAndRelatedFlatEntityMaps, + }: FlatEntityUpdateValidationArgs< + typeof ALL_METADATA_NAME.rowLevelPermissionPredicate + >): FailedFlatEntityValidation { + const { + flatRowLevelPermissionPredicateMaps: optimisticFlatPredicateMaps, + flatFieldMetadataMaps, + flatObjectMetadataMaps, + flatRowLevelPermissionPredicateGroupMaps, + flatRoleMaps, + } = optimisticFlatEntityMapsAndRelatedFlatEntityMaps as Partial<{ + flatRowLevelPermissionPredicateMaps: typeof optimisticFlatEntityMapsAndRelatedFlatEntityMaps.flatRowLevelPermissionPredicateMaps; + flatFieldMetadataMaps: typeof optimisticFlatEntityMapsAndRelatedFlatEntityMaps.flatFieldMetadataMaps; + flatObjectMetadataMaps: typeof optimisticFlatEntityMapsAndRelatedFlatEntityMaps.flatObjectMetadataMaps; + flatRowLevelPermissionPredicateGroupMaps: typeof optimisticFlatEntityMapsAndRelatedFlatEntityMaps.flatRowLevelPermissionPredicateGroupMaps; + flatRoleMaps: typeof optimisticFlatEntityMapsAndRelatedFlatEntityMaps.flatRoleMaps; + }>; + const validationResult: FailedFlatEntityValidation = + { + type: 'update_row_level_permission_predicate', + errors: [], + flatEntityMinimalInformation: { + id: flatEntityId, + }, + }; + + const existingPredicate = optimisticFlatPredicateMaps?.byId[flatEntityId]; + + if (!isDefined(existingPredicate)) { + validationResult.errors.push({ + code: RowLevelPermissionPredicateExceptionCode.ROW_LEVEL_PERMISSION_PREDICATE_NOT_FOUND, + message: t`Row level permission predicate to update not found`, + userFriendlyMessage: msg`Row level permission predicate to update not found`, + }); + + return validationResult; + } + + const updatedPredicate = { + ...existingPredicate, + ...fromFlatEntityPropertiesUpdatesToPartialFlatEntity({ + updates: flatEntityUpdates, + }), + }; + + const fieldMetadata = flatFieldMetadataMaps + ? findFlatEntityByIdInFlatEntityMaps({ + flatEntityId: updatedPredicate.fieldMetadataId, + flatEntityMaps: flatFieldMetadataMaps, + }) + : undefined; + + if (!isDefined(fieldMetadata)) { + validationResult.errors.push({ + code: RowLevelPermissionPredicateExceptionCode.FIELD_METADATA_NOT_FOUND, + message: t`Field metadata not found`, + userFriendlyMessage: msg`Field metadata not found`, + }); + } + + const objectMetadata = flatObjectMetadataMaps + ? findFlatEntityByIdInFlatEntityMaps({ + flatEntityId: updatedPredicate.objectMetadataId, + flatEntityMaps: flatObjectMetadataMaps, + }) + : undefined; + + if (!isDefined(objectMetadata)) { + validationResult.errors.push({ + code: RowLevelPermissionPredicateExceptionCode.OBJECT_METADATA_NOT_FOUND, + message: t`Object metadata not found`, + userFriendlyMessage: msg`Object metadata not found`, + }); + } + + if ( + isDefined(updatedPredicate.rowLevelPermissionPredicateGroupId) && + flatRowLevelPermissionPredicateGroupMaps + ) { + const predicateGroup = findFlatEntityByIdInFlatEntityMaps({ + flatEntityId: updatedPredicate.rowLevelPermissionPredicateGroupId, + flatEntityMaps: flatRowLevelPermissionPredicateGroupMaps, + }); + + if (!isDefined(predicateGroup)) { + validationResult.errors.push({ + code: RowLevelPermissionPredicateExceptionCode.INVALID_ROW_LEVEL_PERMISSION_PREDICATE_DATA, + message: t`Row level permission predicate group not found`, + userFriendlyMessage: msg`Row level permission predicate group not found`, + }); + } + } + + const role = flatRoleMaps + ? findFlatEntityByIdInFlatEntityMaps({ + flatEntityId: updatedPredicate.roleId, + flatEntityMaps: flatRoleMaps, + }) + : undefined; + + if (!isDefined(role)) { + validationResult.errors.push({ + code: RowLevelPermissionPredicateExceptionCode.ROLE_NOT_FOUND, + message: t`Role not found`, + userFriendlyMessage: msg`Role 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 258ca6be06..cb17a8ad21 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 @@ -3,17 +3,19 @@ import { Module } from '@nestjs/common'; import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module'; import { FlatFieldMetadataTypeValidatorService } from 'src/engine/metadata-modules/flat-field-metadata/services/flat-field-metadata-type-validator.service'; import { FlatAgentValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-agent-validator.service'; -import { FlatPageLayoutValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-page-layout-validator.service'; import { FlatCronTriggerValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-cron-trigger-validator.service'; import { FlatDatabaseEventTriggerValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-database-event-trigger-validator.service'; import { FlatFieldMetadataValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-field-metadata-validator.service'; import { FlatIndexValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-index-metadata-validator.service'; import { FlatObjectMetadataValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-object-metadata-validator.service'; import { FlatPageLayoutTabValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-page-layout-tab-validator.service'; +import { FlatPageLayoutValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-page-layout-validator.service'; import { FlatPageLayoutWidgetValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-page-layout-widget-validator.service'; import { FlatRoleTargetValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-role-target-validator.service'; import { FlatRoleValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-role-validator.service'; import { FlatRouteTriggerValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-route-trigger-validator.service'; +import { FlatRowLevelPermissionPredicateGroupValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-row-level-permission-predicate-group-validator.service'; +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 { FlatViewFilterValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-view-filter-validator.service'; @@ -41,6 +43,8 @@ import { FlatViewValidatorService } from 'src/engine/workspace-manager/workspace FlatPageLayoutValidatorService, FlatPageLayoutWidgetValidatorService, FlatPageLayoutTabValidatorService, + FlatRowLevelPermissionPredicateValidatorService, + FlatRowLevelPermissionPredicateGroupValidatorService, ], exports: [ FlatViewValidatorService, @@ -61,6 +65,8 @@ import { FlatViewValidatorService } from 'src/engine/workspace-manager/workspace FlatPageLayoutValidatorService, FlatPageLayoutWidgetValidatorService, FlatPageLayoutTabValidatorService, + FlatRowLevelPermissionPredicateValidatorService, + FlatRowLevelPermissionPredicateGroupValidatorService, ], }) export class WorkspaceMigrationBuilderValidatorsModule {} 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 384c673113..e9d775b5d2 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 @@ -8,12 +8,14 @@ import { WorkspaceMigrationV2DatabaseEventTriggerActionsBuilderService } from 's import { WorkspaceMigrationV2FieldActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/field/workspace-migration-v2-field-actions-builder.service'; import { WorkspaceMigrationV2IndexActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/index/workspace-migration-v2-index-actions-builder.service'; import { WorkspaceMigrationV2ObjectActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/object/workspace-migration-v2-object-actions-builder.service'; -import { WorkspaceMigrationV2PageLayoutActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/page-layout/workspace-migration-v2-page-layout-actions-builder.service'; import { WorkspaceMigrationV2PageLayoutTabActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/page-layout-tab/workspace-migration-v2-page-layout-tab-actions-builder.service'; import { WorkspaceMigrationV2PageLayoutWidgetActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/page-layout-widget/workspace-migration-v2-page-layout-widget-actions-builder.service'; +import { WorkspaceMigrationV2PageLayoutActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/page-layout/workspace-migration-v2-page-layout-actions-builder.service'; import { WorkspaceMigrationV2RoleTargetActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/role-target/workspace-migration-v2-role-target-actions-builder.service'; import { WorkspaceMigrationV2RoleActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/role/workspace-migration-v2-role-actions-builder.service'; import { WorkspaceMigrationV2RouteTriggerActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/route-trigger/workspace-migration-v2-route-trigger-actions-builder.service'; +import { WorkspaceMigrationV2RowLevelPermissionPredicateGroupActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/row-level-permission-predicate-group/workspace-migration-v2-row-level-permission-predicate-group-actions-builder.service'; +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 { WorkspaceMigrationV2ViewFilterActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view-filter/workspace-migration-v2-view-filter-actions-builder.service'; @@ -42,6 +44,8 @@ import { WorkspaceMigrationBuilderValidatorsModule } from 'src/engine/workspace- WorkspaceMigrationV2PageLayoutActionsBuilderService, WorkspaceMigrationV2PageLayoutWidgetActionsBuilderService, WorkspaceMigrationV2PageLayoutTabActionsBuilderService, + WorkspaceMigrationV2RowLevelPermissionPredicateActionsBuilderService, + WorkspaceMigrationV2RowLevelPermissionPredicateGroupActionsBuilderService, ], exports: [ WorkspaceMigrationV2ViewActionsBuilderService, @@ -61,6 +65,8 @@ import { WorkspaceMigrationBuilderValidatorsModule } from 'src/engine/workspace- WorkspaceMigrationV2PageLayoutActionsBuilderService, WorkspaceMigrationV2PageLayoutWidgetActionsBuilderService, WorkspaceMigrationV2PageLayoutTabActionsBuilderService, + WorkspaceMigrationV2RowLevelPermissionPredicateActionsBuilderService, + WorkspaceMigrationV2RowLevelPermissionPredicateGroupActionsBuilderService, FlatFieldMetadataTypeValidatorService, ], }) diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/row-level-permission-predicate-group/services/create-row-level-permission-predicate-group-action-handler.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/row-level-permission-predicate-group/services/create-row-level-permission-predicate-group-action-handler.service.ts new file mode 100644 index 0000000000..a6dc98db6c --- /dev/null +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/row-level-permission-predicate-group/services/create-row-level-permission-predicate-group-action-handler.service.ts @@ -0,0 +1,59 @@ +/* @license Enterprise */ + +import { Injectable } from '@nestjs/common'; + +import { + OptimisticallyApplyActionOnAllFlatEntityMapsArgs, + WorkspaceMigrationRunnerActionHandler, +} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface'; + +import { 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 { RowLevelPermissionPredicateGroupEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate-group.entity'; +import { CreateRowLevelPermissionPredicateGroupAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/row-level-permission-predicate-group/types/workspace-migration-row-level-permission-predicate-group-action-v2.type'; +import { WorkspaceMigrationActionRunnerArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/types/workspace-migration-action-runner-args.type'; + +@Injectable() +export class CreateRowLevelPermissionPredicateGroupActionHandlerService extends WorkspaceMigrationRunnerActionHandler( + 'create_row_level_permission_predicate_group', +) { + optimisticallyApplyActionOnAllFlatEntityMaps({ + action, + allFlatEntityMaps, + }: OptimisticallyApplyActionOnAllFlatEntityMapsArgs): Partial { + const { flatRowLevelPermissionPredicateGroupMaps } = allFlatEntityMaps; + const { rowLevelPermissionPredicateGroup } = action; + + const updatedFlatMaps = addFlatEntityToFlatEntityMapsOrThrow({ + flatEntity: rowLevelPermissionPredicateGroup, + flatEntityMaps: flatRowLevelPermissionPredicateGroupMaps, + }); + + return { + flatRowLevelPermissionPredicateGroupMaps: updatedFlatMaps, + }; + } + + async executeForMetadata( + context: WorkspaceMigrationActionRunnerArgs, + ): Promise { + const { action, queryRunner, workspaceId } = context; + const { rowLevelPermissionPredicateGroup } = action; + + const repository = + queryRunner.manager.getRepository( + RowLevelPermissionPredicateGroupEntity, + ); + + await repository.insert({ + ...rowLevelPermissionPredicateGroup, + 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/row-level-permission-predicate-group/services/delete-row-level-permission-predicate-group-action-handler.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/row-level-permission-predicate-group/services/delete-row-level-permission-predicate-group-action-handler.service.ts new file mode 100644 index 0000000000..429b0e86f9 --- /dev/null +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/row-level-permission-predicate-group/services/delete-row-level-permission-predicate-group-action-handler.service.ts @@ -0,0 +1,59 @@ +/* @license Enterprise */ + +import { Injectable } from '@nestjs/common'; + +import { + OptimisticallyApplyActionOnAllFlatEntityMapsArgs, + WorkspaceMigrationRunnerActionHandler, +} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface'; + +import { 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 { RowLevelPermissionPredicateGroupEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate-group.entity'; +import { DeleteRowLevelPermissionPredicateGroupAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/row-level-permission-predicate-group/types/workspace-migration-row-level-permission-predicate-group-action-v2.type'; +import { WorkspaceMigrationActionRunnerArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/types/workspace-migration-action-runner-args.type'; + +@Injectable() +export class DeleteRowLevelPermissionPredicateGroupActionHandlerService extends WorkspaceMigrationRunnerActionHandler( + 'delete_row_level_permission_predicate_group', +) { + optimisticallyApplyActionOnAllFlatEntityMaps({ + action, + allFlatEntityMaps, + }: OptimisticallyApplyActionOnAllFlatEntityMapsArgs): Partial { + const { flatRowLevelPermissionPredicateGroupMaps } = allFlatEntityMaps; + const { rowLevelPermissionPredicateGroupId } = action; + + const updatedFlatMaps = deleteFlatEntityFromFlatEntityMapsOrThrow({ + entityToDeleteId: rowLevelPermissionPredicateGroupId, + flatEntityMaps: flatRowLevelPermissionPredicateGroupMaps, + }); + + return { + flatRowLevelPermissionPredicateGroupMaps: updatedFlatMaps, + }; + } + + async executeForMetadata( + context: WorkspaceMigrationActionRunnerArgs, + ): Promise { + const { action, queryRunner, workspaceId } = context; + const { rowLevelPermissionPredicateGroupId } = action; + + const repository = + queryRunner.manager.getRepository( + RowLevelPermissionPredicateGroupEntity, + ); + + await repository.delete({ + id: rowLevelPermissionPredicateGroupId, + 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/row-level-permission-predicate-group/services/update-row-level-permission-predicate-group-action-handler.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/row-level-permission-predicate-group/services/update-row-level-permission-predicate-group-action-handler.service.ts new file mode 100644 index 0000000000..a323c1dff2 --- /dev/null +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/row-level-permission-predicate-group/services/update-row-level-permission-predicate-group-action-handler.service.ts @@ -0,0 +1,70 @@ +/* @license Enterprise */ + +import { Injectable } from '@nestjs/common'; + +import { + OptimisticallyApplyActionOnAllFlatEntityMapsArgs, + WorkspaceMigrationRunnerActionHandler, +} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface'; + +import { 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 { RowLevelPermissionPredicateGroupEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate-group.entity'; +import { UpdateRowLevelPermissionPredicateGroupAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/row-level-permission-predicate-group/types/workspace-migration-row-level-permission-predicate-group-action-v2.type'; +import { 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 UpdateRowLevelPermissionPredicateGroupActionHandlerService extends WorkspaceMigrationRunnerActionHandler( + 'update_row_level_permission_predicate_group', +) { + optimisticallyApplyActionOnAllFlatEntityMaps({ + action, + allFlatEntityMaps, + }: OptimisticallyApplyActionOnAllFlatEntityMapsArgs): Partial { + const { flatRowLevelPermissionPredicateGroupMaps } = allFlatEntityMaps; + const { rowLevelPermissionPredicateGroupId } = action; + + const existingGroup = findFlatEntityByIdInFlatEntityMapsOrThrow({ + flatEntityId: rowLevelPermissionPredicateGroupId, + flatEntityMaps: flatRowLevelPermissionPredicateGroupMaps, + }); + + const updatedGroup = { + ...existingGroup, + ...fromFlatEntityPropertiesUpdatesToPartialFlatEntity(action), + }; + + const updatedFlatMaps = replaceFlatEntityInFlatEntityMapsOrThrow({ + flatEntity: updatedGroup, + flatEntityMaps: flatRowLevelPermissionPredicateGroupMaps, + }); + + return { + flatRowLevelPermissionPredicateGroupMaps: updatedFlatMaps, + }; + } + + async executeForMetadata( + context: WorkspaceMigrationActionRunnerArgs, + ): Promise { + const { action, queryRunner } = context; + const { rowLevelPermissionPredicateGroupId } = action; + + const repository = + queryRunner.manager.getRepository( + RowLevelPermissionPredicateGroupEntity, + ); + + const update = fromFlatEntityPropertiesUpdatesToPartialFlatEntity(action); + + await repository.update(rowLevelPermissionPredicateGroupId, 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/row-level-permission-predicate/services/create-row-level-permission-predicate-action-handler.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/row-level-permission-predicate/services/create-row-level-permission-predicate-action-handler.service.ts new file mode 100644 index 0000000000..61cfd66150 --- /dev/null +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/row-level-permission-predicate/services/create-row-level-permission-predicate-action-handler.service.ts @@ -0,0 +1,59 @@ +/* @license Enterprise */ + +import { Injectable } from '@nestjs/common'; + +import { + OptimisticallyApplyActionOnAllFlatEntityMapsArgs, + WorkspaceMigrationRunnerActionHandler, +} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface'; + +import { 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 { RowLevelPermissionPredicateEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate.entity'; +import { CreateRowLevelPermissionPredicateAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/row-level-permission-predicate/types/workspace-migration-row-level-permission-predicate-action-v2.type'; +import { WorkspaceMigrationActionRunnerArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/types/workspace-migration-action-runner-args.type'; + +@Injectable() +export class CreateRowLevelPermissionPredicateActionHandlerService extends WorkspaceMigrationRunnerActionHandler( + 'create_row_level_permission_predicate', +) { + optimisticallyApplyActionOnAllFlatEntityMaps({ + action, + allFlatEntityMaps, + }: OptimisticallyApplyActionOnAllFlatEntityMapsArgs): Partial { + const { flatRowLevelPermissionPredicateMaps } = allFlatEntityMaps; + const { rowLevelPermissionPredicate } = action; + + const updatedFlatMaps = addFlatEntityToFlatEntityMapsOrThrow({ + flatEntity: rowLevelPermissionPredicate, + flatEntityMaps: flatRowLevelPermissionPredicateMaps, + }); + + return { + flatRowLevelPermissionPredicateMaps: updatedFlatMaps, + }; + } + + async executeForMetadata( + context: WorkspaceMigrationActionRunnerArgs, + ): Promise { + const { action, queryRunner, workspaceId } = context; + const { rowLevelPermissionPredicate } = action; + + const repository = + queryRunner.manager.getRepository( + RowLevelPermissionPredicateEntity, + ); + + await repository.insert({ + ...rowLevelPermissionPredicate, + 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/row-level-permission-predicate/services/delete-row-level-permission-predicate-action-handler.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/row-level-permission-predicate/services/delete-row-level-permission-predicate-action-handler.service.ts new file mode 100644 index 0000000000..9c6e06895a --- /dev/null +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/row-level-permission-predicate/services/delete-row-level-permission-predicate-action-handler.service.ts @@ -0,0 +1,59 @@ +/* @license Enterprise */ + +import { Injectable } from '@nestjs/common'; + +import { + OptimisticallyApplyActionOnAllFlatEntityMapsArgs, + WorkspaceMigrationRunnerActionHandler, +} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface'; + +import { 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 { RowLevelPermissionPredicateEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate.entity'; +import { DeleteRowLevelPermissionPredicateAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/row-level-permission-predicate/types/workspace-migration-row-level-permission-predicate-action-v2.type'; +import { WorkspaceMigrationActionRunnerArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/types/workspace-migration-action-runner-args.type'; + +@Injectable() +export class DeleteRowLevelPermissionPredicateActionHandlerService extends WorkspaceMigrationRunnerActionHandler( + 'delete_row_level_permission_predicate', +) { + optimisticallyApplyActionOnAllFlatEntityMaps({ + action, + allFlatEntityMaps, + }: OptimisticallyApplyActionOnAllFlatEntityMapsArgs): Partial { + const { flatRowLevelPermissionPredicateMaps } = allFlatEntityMaps; + const { rowLevelPermissionPredicateId } = action; + + const updatedFlatMaps = deleteFlatEntityFromFlatEntityMapsOrThrow({ + entityToDeleteId: rowLevelPermissionPredicateId, + flatEntityMaps: flatRowLevelPermissionPredicateMaps, + }); + + return { + flatRowLevelPermissionPredicateMaps: updatedFlatMaps, + }; + } + + async executeForMetadata( + context: WorkspaceMigrationActionRunnerArgs, + ): Promise { + const { action, queryRunner, workspaceId } = context; + const { rowLevelPermissionPredicateId } = action; + + const repository = + queryRunner.manager.getRepository( + RowLevelPermissionPredicateEntity, + ); + + await repository.delete({ + id: rowLevelPermissionPredicateId, + 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/row-level-permission-predicate/services/update-row-level-permission-predicate-action-handler.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/row-level-permission-predicate/services/update-row-level-permission-predicate-action-handler.service.ts new file mode 100644 index 0000000000..243131999f --- /dev/null +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/row-level-permission-predicate/services/update-row-level-permission-predicate-action-handler.service.ts @@ -0,0 +1,70 @@ +/* @license Enterprise */ + +import { Injectable } from '@nestjs/common'; + +import { + OptimisticallyApplyActionOnAllFlatEntityMapsArgs, + WorkspaceMigrationRunnerActionHandler, +} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface'; + +import { 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 { RowLevelPermissionPredicateEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate.entity'; +import { UpdateRowLevelPermissionPredicateAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/row-level-permission-predicate/types/workspace-migration-row-level-permission-predicate-action-v2.type'; +import { 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 UpdateRowLevelPermissionPredicateActionHandlerService extends WorkspaceMigrationRunnerActionHandler( + 'update_row_level_permission_predicate', +) { + optimisticallyApplyActionOnAllFlatEntityMaps({ + action, + allFlatEntityMaps, + }: OptimisticallyApplyActionOnAllFlatEntityMapsArgs): Partial { + const { flatRowLevelPermissionPredicateMaps } = allFlatEntityMaps; + const { rowLevelPermissionPredicateId } = action; + + const existingPredicate = findFlatEntityByIdInFlatEntityMapsOrThrow({ + flatEntityId: rowLevelPermissionPredicateId, + flatEntityMaps: flatRowLevelPermissionPredicateMaps, + }); + + const updatedPredicate = { + ...existingPredicate, + ...fromFlatEntityPropertiesUpdatesToPartialFlatEntity(action), + }; + + const updatedFlatMaps = replaceFlatEntityInFlatEntityMapsOrThrow({ + flatEntity: updatedPredicate, + flatEntityMaps: flatRowLevelPermissionPredicateMaps, + }); + + return { + flatRowLevelPermissionPredicateMaps: updatedFlatMaps, + }; + } + + async executeForMetadata( + context: WorkspaceMigrationActionRunnerArgs, + ): Promise { + const { action, queryRunner } = context; + const { rowLevelPermissionPredicateId } = action; + + const repository = + queryRunner.manager.getRepository( + RowLevelPermissionPredicateEntity, + ); + + const update = fromFlatEntityPropertiesUpdatesToPartialFlatEntity(action); + + await repository.update(rowLevelPermissionPredicateId, 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 362590e34b..7f023515e8 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 @@ -4,9 +4,6 @@ import { WorkspaceSchemaManagerModule } from 'src/engine/twenty-orm/workspace-sc import { CreateAgentActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/agent/services/create-agent-action-handler.service'; import { DeleteAgentActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/agent/services/delete-agent-action-handler.service'; import { UpdateAgentActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/agent/services/update-agent-action-handler.service'; -import { CreatePageLayoutActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/page-layout/services/create-page-layout-action-handler.service'; -import { DeletePageLayoutActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/page-layout/services/delete-page-layout-action-handler.service'; -import { UpdatePageLayoutActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/page-layout/services/update-page-layout-action-handler.service'; import { CreateCronTriggerActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/cron-trigger/services/create-cron-trigger-action-handler.service'; import { DeleteCronTriggerActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/cron-trigger/services/delete-cron-trigger-action-handler.service'; import { UpdateCronTriggerActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/cron-trigger/services/update-cron-trigger-action-handler.service'; @@ -27,6 +24,9 @@ import { UpdatePageLayoutTabActionHandlerService } from 'src/engine/workspace-ma import { CreatePageLayoutWidgetActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/page-layout-widget/services/create-page-layout-widget-action-handler.service'; import { DeletePageLayoutWidgetActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/page-layout-widget/services/delete-page-layout-widget-action-handler.service'; import { UpdatePageLayoutWidgetActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/page-layout-widget/services/update-page-layout-widget-action-handler.service'; +import { CreatePageLayoutActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/page-layout/services/create-page-layout-action-handler.service'; +import { DeletePageLayoutActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/page-layout/services/delete-page-layout-action-handler.service'; +import { UpdatePageLayoutActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/page-layout/services/update-page-layout-action-handler.service'; import { CreateRoleTargetActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/role-target/services/create-role-target-action-handler.service'; import { DeleteRoleTargetActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/role-target/services/delete-role-target-action-handler.service'; import { UpdateRoleTargetActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/role-target/services/update-role-target-action-handler.service'; @@ -36,6 +36,12 @@ import { UpdateRoleActionHandlerService } from 'src/engine/workspace-manager/wor import { CreateRouteTriggerActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/route-trigger/services/create-route-trigger-action-handler.service'; import { DeleteRouteTriggerActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/route-trigger/services/delete-route-trigger-action-handler.service'; import { UpdateRouteTriggerActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/route-trigger/services/update-route-trigger-action-handler.service'; +import { CreateRowLevelPermissionPredicateGroupActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/row-level-permission-predicate-group/services/create-row-level-permission-predicate-group-action-handler.service'; +import { DeleteRowLevelPermissionPredicateGroupActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/row-level-permission-predicate-group/services/delete-row-level-permission-predicate-group-action-handler.service'; +import { UpdateRowLevelPermissionPredicateGroupActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/row-level-permission-predicate-group/services/update-row-level-permission-predicate-group-action-handler.service'; +import { CreateRowLevelPermissionPredicateActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/row-level-permission-predicate/services/create-row-level-permission-predicate-action-handler.service'; +import { DeleteRowLevelPermissionPredicateActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/row-level-permission-predicate/services/delete-row-level-permission-predicate-action-handler.service'; +import { UpdateRowLevelPermissionPredicateActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/row-level-permission-predicate/services/update-row-level-permission-predicate-action-handler.service'; import { CreateServerlessFunctionActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/serverless-function/services/create-serverless-function-action-handler.service'; import { DeleteServerlessFunctionActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/serverless-function/services/delete-serverless-function-action-handler.service'; import { UpdateServerlessFunctionActionHandlerService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/serverless-function/services/update-serverless-function-action-handler.service'; @@ -121,6 +127,14 @@ import { UpdateViewActionHandlerService } from 'src/engine/workspace-manager/wor CreatePageLayoutTabActionHandlerService, UpdatePageLayoutTabActionHandlerService, DeletePageLayoutTabActionHandlerService, + + CreateRowLevelPermissionPredicateActionHandlerService, + UpdateRowLevelPermissionPredicateActionHandlerService, + DeleteRowLevelPermissionPredicateActionHandlerService, + + CreateRowLevelPermissionPredicateGroupActionHandlerService, + UpdateRowLevelPermissionPredicateGroupActionHandlerService, + DeleteRowLevelPermissionPredicateGroupActionHandlerService, ], }) export class WorkspaceSchemaMigrationRunnerActionHandlersModule {} 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 32fd00f54d..1e42b92a88 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 @@ -17,6 +17,8 @@ exports[`View Field Resolver - Failing Create Many Operations - v2 should accumu "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [ @@ -91,6 +93,8 @@ exports[`View Field Resolver - Failing Create Many Operations - v2 should accumu "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 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 04ea3a36df..4fe3a46f92 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 @@ -17,6 +17,8 @@ exports[`View Field Resolver - Failing Create Operation - v2 Should fail to crea "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [ @@ -54,6 +56,8 @@ exports[`View Field Resolver - Failing Create Operation - v2 Should fail to crea "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -85,6 +89,8 @@ exports[`View Field Resolver - Failing Create Operation - v2 should fail to crea "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [ @@ -122,6 +128,8 @@ exports[`View Field Resolver - Failing Create Operation - v2 should fail to crea "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -153,6 +161,8 @@ exports[`View Field Resolver - Failing Create Operation - v2 should fail to crea "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [ @@ -190,6 +200,8 @@ exports[`View Field Resolver - Failing Create Operation - v2 should fail to crea "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 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 a936ddd2fa..b23e961315 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 @@ -17,6 +17,8 @@ exports[`View Field Resolver - Successful object metadata identifier update side "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [ @@ -54,6 +56,8 @@ exports[`View Field Resolver - Successful object metadata identifier update side "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -85,6 +89,8 @@ exports[`View Field Resolver - Successful object metadata identifier update side "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [ @@ -122,6 +128,8 @@ exports[`View Field Resolver - Successful object metadata identifier update side "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -153,6 +161,8 @@ exports[`View Field Resolver - Successful object metadata identifier update side "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [ @@ -188,6 +198,8 @@ exports[`View Field Resolver - Successful object metadata identifier update side "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -219,6 +231,8 @@ exports[`View Field Resolver - Successful object metadata identifier update side "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [ @@ -256,6 +270,8 @@ exports[`View Field Resolver - Successful object metadata identifier update side "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -287,6 +303,8 @@ exports[`View Field Resolver - Successful object metadata identifier update side "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [ @@ -324,6 +342,8 @@ exports[`View Field Resolver - Successful object metadata identifier update side "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, 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 16b959adba..8626c26a01 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 @@ -33,6 +33,8 @@ exports[`Agent creation should fail when computed name already exists 1`] = ` "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -53,6 +55,8 @@ exports[`Agent creation should fail when computed name already exists 1`] = ` "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -100,6 +104,8 @@ exports[`Agent creation should fail when label is empty string 1`] = ` "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -120,6 +126,8 @@ exports[`Agent creation should fail when label is empty string 1`] = ` "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -181,6 +189,8 @@ exports[`Agent creation should fail when modelId is empty string 1`] = ` "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -201,6 +211,8 @@ exports[`Agent creation should fail when modelId is empty string 1`] = ` "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -262,6 +274,8 @@ exports[`Agent creation should fail when prompt is empty string 1`] = ` "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -282,6 +296,8 @@ exports[`Agent creation should fail when prompt is empty string 1`] = ` "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -343,6 +359,8 @@ exports[`Agent creation should fail when responseFormat has invalid type 1`] = ` "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -363,6 +381,8 @@ exports[`Agent creation should fail when responseFormat has invalid type 1`] = ` "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -410,6 +430,8 @@ exports[`Agent creation should fail when responseFormat type is json but schema "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -430,6 +452,8 @@ exports[`Agent creation should fail when responseFormat type is json but schema "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -477,6 +501,8 @@ exports[`Agent creation should fail when responseFormat type is text but schema "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -497,6 +523,8 @@ exports[`Agent creation should fail when responseFormat type is text but schema "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 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 ef14792ff7..c16bd492b2 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 @@ -33,6 +33,8 @@ exports[`Agent deletion should fail should fail when attempting to delete a stan "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -53,6 +55,8 @@ exports[`Agent deletion should fail should fail when attempting to delete a stan "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 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 a05b98d4bc..f8d17f24e5 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 @@ -32,6 +32,8 @@ exports[`Agent update should fail should fail when attempting to update a standa "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -52,6 +54,8 @@ exports[`Agent update should fail should fail when attempting to update a standa "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -120,6 +124,8 @@ exports[`Agent update should fail when settings null required properties 1`] = ` "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -140,6 +146,8 @@ exports[`Agent update should fail when settings null required properties 1`] = ` "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -186,6 +194,8 @@ exports[`Agent update should fail when updating label to one that already exists "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -206,6 +216,8 @@ exports[`Agent update should fail when updating label to one that already exists "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -252,6 +264,8 @@ exports[`Agent update should fail when updating responseFormat type to json with "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -272,6 +286,8 @@ exports[`Agent update should fail when updating responseFormat type to json with "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -318,6 +334,8 @@ exports[`Agent update should fail when updating responseFormat type to text with "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -338,6 +356,8 @@ exports[`Agent update should fail when updating responseFormat type to text with "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -384,6 +404,8 @@ exports[`Agent update should fail when updating responseFormat with invalid type "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -404,6 +426,8 @@ exports[`Agent update should fail when updating responseFormat with invalid type "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -450,6 +474,8 @@ exports[`Agent update should fail when updating with empty label 1`] = ` "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -470,6 +496,8 @@ exports[`Agent update should fail when updating with empty label 1`] = ` "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -516,6 +544,8 @@ exports[`Agent update should fail when updating with empty modelId 1`] = ` "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -536,6 +566,8 @@ exports[`Agent update should fail when updating with empty modelId 1`] = ` "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -582,6 +614,8 @@ exports[`Agent update should fail when updating with empty prompt 1`] = ` "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -602,6 +636,8 @@ exports[`Agent update should fail when updating with empty prompt 1`] = ` "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 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 8749b7c5f4..f0823a7220 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 @@ -35,6 +35,8 @@ exports[`createOne FieldMetadataService name/label sync should return an error w "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -55,6 +57,8 @@ exports[`createOne FieldMetadataService name/label sync should return an error w "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 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 2ce90f0890..9b5f133c66 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 @@ -36,6 +36,8 @@ exports[`Failing create field metadata tests suite should fail to create NUMERIC "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -56,6 +58,8 @@ exports[`Failing create field metadata tests suite should fail to create NUMERIC "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -107,6 +111,8 @@ exports[`Failing create field metadata tests suite should fail to create POSITIO "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -127,6 +133,8 @@ exports[`Failing create field metadata tests suite should fail to create POSITIO "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -171,6 +179,8 @@ exports[`Failing create field metadata tests suite should fail to create TS_VECT "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -191,6 +201,8 @@ exports[`Failing create field metadata tests suite should fail to create TS_VECT "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 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 b555a36f49..139c700e05 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 @@ -35,6 +35,8 @@ exports[`updateOne failing update should not update a field name if it is not sy "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -55,6 +57,8 @@ exports[`updateOne failing update should not update a field name if it is not sy "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -111,6 +115,8 @@ exports[`updateOne failing update should throw if the field name is not availabl "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -131,6 +137,8 @@ exports[`updateOne failing update should throw if the field name is not availabl "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 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 9dd2242d18..5d4efc58fb 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 @@ -36,6 +36,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -56,6 +58,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -107,6 +111,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -127,6 +133,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -178,6 +186,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -198,6 +208,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -264,6 +276,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -284,6 +298,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -350,6 +366,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -370,6 +388,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -436,6 +456,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -456,6 +478,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -522,6 +546,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -542,6 +568,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -593,6 +621,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -613,6 +643,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -664,6 +696,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -684,6 +718,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -741,6 +777,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -761,6 +799,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -812,6 +852,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -832,6 +874,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -883,6 +927,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -903,6 +949,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -954,6 +1002,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -974,6 +1024,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1043,6 +1095,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1063,6 +1117,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1114,6 +1170,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1134,6 +1192,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1185,6 +1245,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1205,6 +1267,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1256,6 +1320,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1276,6 +1342,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1327,6 +1395,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1347,6 +1417,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1398,6 +1470,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1418,6 +1492,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1469,6 +1545,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1489,6 +1567,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1540,6 +1620,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1560,6 +1642,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1616,6 +1700,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1636,6 +1722,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1686,6 +1774,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1706,6 +1796,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1757,6 +1849,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1777,6 +1871,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1827,6 +1923,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1847,6 +1945,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1904,6 +2004,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1924,6 +2026,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1975,6 +2079,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1995,6 +2101,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -2046,6 +2154,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -2066,6 +2176,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -2117,6 +2229,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -2137,6 +2251,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -2194,6 +2310,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -2214,6 +2332,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -2265,6 +2385,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -2285,6 +2407,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -2336,6 +2460,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -2356,6 +2482,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -2413,6 +2541,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -2433,6 +2563,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -2483,6 +2615,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -2503,6 +2637,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -2553,6 +2689,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -2573,6 +2711,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -2624,6 +2764,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -2644,6 +2786,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -2695,6 +2839,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -2715,6 +2861,8 @@ exports[`Failing create field metadata MULTI_SELECT tests suite v2 Create should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -2772,6 +2920,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -2792,6 +2942,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -2843,6 +2995,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -2863,6 +3017,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -2914,6 +3070,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -2934,6 +3092,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -3000,6 +3160,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -3020,6 +3182,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -3086,6 +3250,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -3106,6 +3272,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -3172,6 +3340,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -3192,6 +3362,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -3258,6 +3430,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -3278,6 +3452,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -3329,6 +3505,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -3349,6 +3527,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -3406,6 +3586,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -3426,6 +3608,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -3477,6 +3661,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -3497,6 +3683,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -3548,6 +3736,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -3568,6 +3758,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -3619,6 +3811,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -3639,6 +3833,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -3690,6 +3886,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -3710,6 +3908,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -3761,6 +3961,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -3781,6 +3983,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -3832,6 +4036,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -3852,6 +4058,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -3903,6 +4111,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -3923,6 +4133,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -3974,6 +4186,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -3994,6 +4208,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -4045,6 +4261,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -4065,6 +4283,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -4121,6 +4341,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -4141,6 +4363,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -4191,6 +4415,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -4211,6 +4437,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -4262,6 +4490,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -4282,6 +4512,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -4332,6 +4564,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -4352,6 +4586,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -4409,6 +4645,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -4429,6 +4667,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -4480,6 +4720,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -4500,6 +4742,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -4551,6 +4795,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -4571,6 +4817,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -4622,6 +4870,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -4642,6 +4892,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -4699,6 +4951,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -4719,6 +4973,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -4770,6 +5026,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -4790,6 +5048,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -4841,6 +5101,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -4861,6 +5123,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -4918,6 +5182,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -4938,6 +5204,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -4988,6 +5256,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -5008,6 +5278,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -5058,6 +5330,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -5078,6 +5352,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -5129,6 +5405,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -5149,6 +5427,8 @@ exports[`Failing create field metadata SELECT tests suite v2 Create should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 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 43ef8ae2e4..e7a15cf7ee 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 @@ -36,6 +36,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -56,6 +58,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -107,6 +111,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -127,6 +133,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -178,6 +186,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -198,6 +208,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -264,6 +276,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -284,6 +298,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -350,6 +366,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -370,6 +388,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -436,6 +456,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -456,6 +478,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -522,6 +546,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -542,6 +568,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -593,6 +621,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -613,6 +643,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -664,6 +696,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -684,6 +718,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -741,6 +777,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -761,6 +799,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -812,6 +852,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -832,6 +874,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -883,6 +927,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -903,6 +949,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -954,6 +1002,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -974,6 +1024,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1043,6 +1095,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1063,6 +1117,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1114,6 +1170,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1134,6 +1192,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1185,6 +1245,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1205,6 +1267,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1256,6 +1320,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1276,6 +1342,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1327,6 +1395,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1347,6 +1417,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1398,6 +1470,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1418,6 +1492,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1469,6 +1545,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1489,6 +1567,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1540,6 +1620,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1560,6 +1642,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1616,6 +1700,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1636,6 +1722,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1686,6 +1774,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1706,6 +1796,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1756,6 +1848,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1776,6 +1870,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1833,6 +1929,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1853,6 +1951,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1904,6 +2004,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1924,6 +2026,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1975,6 +2079,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1995,6 +2101,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -2046,6 +2154,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -2066,6 +2176,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -2123,6 +2235,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -2143,6 +2257,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -2194,6 +2310,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -2214,6 +2332,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -2265,6 +2385,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -2285,6 +2407,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -2342,6 +2466,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -2362,6 +2488,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -2412,6 +2540,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -2432,6 +2562,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -2482,6 +2614,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -2502,6 +2636,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -2553,6 +2689,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -2573,6 +2711,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -2624,6 +2764,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -2644,6 +2786,8 @@ exports[`Failing update field metadata MULTI_SELECT tests suite v2 Update should "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -2701,6 +2845,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -2721,6 +2867,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -2772,6 +2920,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -2792,6 +2942,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -2843,6 +2995,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -2863,6 +3017,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -2929,6 +3085,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -2949,6 +3107,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -3015,6 +3175,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -3035,6 +3197,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -3101,6 +3265,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -3121,6 +3287,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -3187,6 +3355,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -3207,6 +3377,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -3258,6 +3430,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -3278,6 +3452,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -3335,6 +3511,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -3355,6 +3533,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -3406,6 +3586,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -3426,6 +3608,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -3477,6 +3661,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -3497,6 +3683,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -3548,6 +3736,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -3568,6 +3758,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -3619,6 +3811,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -3639,6 +3833,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -3690,6 +3886,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -3710,6 +3908,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -3761,6 +3961,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -3781,6 +3983,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -3832,6 +4036,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -3852,6 +4058,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -3903,6 +4111,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -3923,6 +4133,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -3974,6 +4186,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -3994,6 +4208,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -4050,6 +4266,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -4070,6 +4288,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -4120,6 +4340,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -4140,6 +4362,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -4190,6 +4414,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -4210,6 +4436,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -4267,6 +4495,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -4287,6 +4517,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -4338,6 +4570,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -4358,6 +4592,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -4409,6 +4645,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -4429,6 +4667,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -4480,6 +4720,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -4500,6 +4742,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -4557,6 +4801,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -4577,6 +4823,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -4628,6 +4876,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -4648,6 +4898,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -4699,6 +4951,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -4719,6 +4973,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -4776,6 +5032,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -4796,6 +5054,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -4846,6 +5106,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -4866,6 +5128,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -4916,6 +5180,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -4936,6 +5202,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -4987,6 +5255,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -5007,6 +5277,8 @@ exports[`Failing update field metadata SELECT tests suite v2 Update should fail "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 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 06dd0bbc56..a91a711c67 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 @@ -78,6 +78,8 @@ exports[`updateOne FieldMetadataService morph relation fields v2 - Add one targe "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -98,6 +100,8 @@ exports[`updateOne FieldMetadataService morph relation fields v2 - Add one targe "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -178,6 +182,8 @@ exports[`updateOne FieldMetadataService morph relation fields v2 - Add one targe "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -198,6 +204,8 @@ exports[`updateOne FieldMetadataService morph relation fields v2 - Add one targe "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 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 447627458f..e60735edca 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 @@ -83,6 +83,8 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 Morh re "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -103,6 +105,8 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 Morh re "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -152,6 +156,8 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 Morh re "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -172,6 +178,8 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 Morh re "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -215,6 +223,8 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -235,6 +245,8 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -278,6 +290,8 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -298,6 +312,8 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -369,6 +385,8 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -389,6 +407,8 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -454,6 +474,8 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -474,6 +496,8 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -539,6 +563,8 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -559,6 +585,8 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -630,6 +658,8 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -650,6 +680,8 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -701,6 +733,8 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -721,6 +755,8 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -772,6 +808,8 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -792,6 +830,8 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -842,6 +882,8 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -862,6 +904,8 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -905,6 +949,8 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -925,6 +971,8 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -968,6 +1016,8 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -988,6 +1038,8 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1031,6 +1083,8 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1051,6 +1105,8 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1097,6 +1153,8 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1117,6 +1175,8 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1161,6 +1221,8 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1181,6 +1243,8 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1224,6 +1288,8 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1244,6 +1310,8 @@ exports[`failing createOne FieldMetadataService morph relation fields v2 it shou "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 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 b4c29ee003..3e64139870 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 @@ -35,6 +35,8 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE (rela "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -55,6 +57,8 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE (rela "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -104,6 +108,8 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE (rela "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -124,6 +130,8 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE (rela "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -179,6 +187,8 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE (rela "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -199,6 +209,8 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE (rela "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -248,6 +260,8 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE (rela "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -268,6 +282,8 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE (rela "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -323,6 +339,8 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE (rela "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -343,6 +361,8 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE (rela "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -392,6 +412,8 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE (rela "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -412,6 +434,8 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE (rela "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -461,6 +485,8 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE (rela "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -481,6 +507,8 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE (rela "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -529,6 +557,8 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE (rela "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -549,6 +579,8 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE (rela "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -630,6 +662,8 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE when "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -650,6 +684,8 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE when "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -699,6 +735,8 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE when "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -719,6 +757,8 @@ exports[`Field metadata relation creation should fail relation MANY_TO_ONE when "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -785,6 +825,8 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY (rela "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -805,6 +847,8 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY (rela "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -872,6 +916,8 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY (rela "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -892,6 +938,8 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY (rela "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -965,6 +1013,8 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY (rela "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -985,6 +1035,8 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY (rela "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1052,6 +1104,8 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY (rela "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1072,6 +1126,8 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY (rela "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1145,6 +1201,8 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY (rela "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1165,6 +1223,8 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY (rela "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1216,6 +1276,8 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY (rela "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1236,6 +1298,8 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY (rela "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1287,6 +1351,8 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY (rela "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1307,6 +1373,8 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY (rela "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1357,6 +1425,8 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY (rela "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1377,6 +1447,8 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY (rela "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1460,6 +1532,8 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY when "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1480,6 +1554,8 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY when "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1531,6 +1607,8 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY when "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1551,6 +1629,8 @@ exports[`Field metadata relation creation should fail relation ONE_TO_MANY when "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1601,6 +1681,8 @@ exports[`Field metadata relation creation should fail should fail when creating "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1621,6 +1703,8 @@ exports[`Field metadata relation creation should fail should fail when creating "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1686,6 +1770,8 @@ exports[`Field metadata relation creation should fail should fail when creating "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1706,6 +1792,8 @@ exports[`Field metadata relation creation should fail should fail when creating "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1787,6 +1875,8 @@ exports[`Field metadata relation creation should fail should fail when creating "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1807,6 +1897,8 @@ exports[`Field metadata relation creation should fail should fail when creating "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1888,6 +1980,8 @@ exports[`Field metadata relation creation should fail should fail when creating "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -1908,6 +2002,8 @@ exports[`Field metadata relation creation should fail should fail when creating "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 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 04dd264340..cf4f4278fd 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 @@ -42,6 +42,8 @@ exports[`Field metadata relation update should fail relation when name is not in "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -62,6 +64,8 @@ exports[`Field metadata relation update should fail relation when name is not in "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 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 dbd370e6c2..ede7b1ee65 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 @@ -33,6 +33,8 @@ exports[`failing createOne unique field metadata should fail for composite type "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -53,6 +55,8 @@ exports[`failing createOne unique field metadata should fail for composite type "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -100,6 +104,8 @@ exports[`failing createOne unique field metadata should fail when field has cust "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -120,6 +126,8 @@ exports[`failing createOne unique field metadata should fail when field has cust "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 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 9df2855a9f..9410eff6cf 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 @@ -361,6 +361,8 @@ exports[`Object metadata creation should fail v2 when labelPlural contains only "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [ { @@ -649,6 +651,8 @@ exports[`Object metadata creation should fail v2 when labelPlural contains only "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1024,6 +1028,8 @@ exports[`Object metadata creation should fail v2 when labelPlural exceeds maximu "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [ { @@ -1312,6 +1318,8 @@ exports[`Object metadata creation should fail v2 when labelPlural exceeds maximu "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -1698,6 +1706,8 @@ exports[`Object metadata creation should fail v2 when labelSingular contains onl "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [ { @@ -1986,6 +1996,8 @@ exports[`Object metadata creation should fail v2 when labelSingular contains onl "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -2361,6 +2373,8 @@ exports[`Object metadata creation should fail v2 when labelSingular exceeds maxi "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [ { @@ -2649,6 +2663,8 @@ exports[`Object metadata creation should fail v2 when labelSingular exceeds maxi "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -3035,6 +3051,8 @@ exports[`Object metadata creation should fail v2 when labels are identical 1`] = "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [ { @@ -3323,6 +3341,8 @@ exports[`Object metadata creation should fail v2 when labels are identical 1`] = "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -3698,6 +3718,8 @@ exports[`Object metadata creation should fail v2 when labels with whitespaces re "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [ { @@ -3986,6 +4008,8 @@ exports[`Object metadata creation should fail v2 when labels with whitespaces re "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -4391,6 +4415,8 @@ exports[`Object metadata creation should fail v2 when name exceeds maximum lengt "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [ { @@ -4679,6 +4705,8 @@ exports[`Object metadata creation should fail v2 when name exceeds maximum lengt "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -5054,6 +5082,8 @@ exports[`Object metadata creation should fail v2 when namePlural has invalid cha "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [ { @@ -5342,6 +5372,8 @@ exports[`Object metadata creation should fail v2 when namePlural has invalid cha "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -5717,6 +5749,8 @@ exports[`Object metadata creation should fail v2 when namePlural is a reserved k "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [ { @@ -6005,6 +6039,8 @@ exports[`Object metadata creation should fail v2 when namePlural is a reserved k "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -6397,6 +6433,8 @@ exports[`Object metadata creation should fail v2 when namePlural is not camelCas "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [ { @@ -6685,6 +6723,8 @@ exports[`Object metadata creation should fail v2 when namePlural is not camelCas "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -7126,6 +7166,8 @@ exports[`Object metadata creation should fail v2 when nameSingular contains only "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [ { @@ -7414,6 +7456,8 @@ exports[`Object metadata creation should fail v2 when nameSingular contains only "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -7843,6 +7887,8 @@ exports[`Object metadata creation should fail v2 when nameSingular contains only "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [ { @@ -8131,6 +8177,8 @@ exports[`Object metadata creation should fail v2 when nameSingular contains only "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -8542,6 +8590,8 @@ exports[`Object metadata creation should fail v2 when nameSingular has invalid c "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [ { @@ -8830,6 +8880,8 @@ exports[`Object metadata creation should fail v2 when nameSingular has invalid c "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -9229,6 +9281,8 @@ exports[`Object metadata creation should fail v2 when nameSingular is a reserved "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [ { @@ -9517,6 +9571,8 @@ exports[`Object metadata creation should fail v2 when nameSingular is a reserved "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -9969,6 +10025,8 @@ exports[`Object metadata creation should fail v2 when nameSingular is not camelC "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [ { @@ -10257,6 +10315,8 @@ exports[`Object metadata creation should fail v2 when nameSingular is not camelC "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -10632,6 +10692,8 @@ exports[`Object metadata creation should fail v2 when names are identical 1`] = "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [ { @@ -10920,6 +10982,8 @@ exports[`Object metadata creation should fail v2 when names are identical 1`] = "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -11295,6 +11359,8 @@ exports[`Object metadata creation should fail v2 when names with whitespaces res "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [ { @@ -11583,6 +11649,8 @@ exports[`Object metadata creation should fail v2 when names with whitespaces res "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 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 55deee8037..0fd1bee333 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 @@ -35,6 +35,8 @@ exports[`Object metadata update should fail when labelIdentifier is a UUID field "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [ @@ -72,6 +74,8 @@ exports[`Object metadata update should fail when labelIdentifier is a UUID field "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -122,6 +126,8 @@ exports[`Object metadata update should fail when labelIdentifier is not a TEXT o "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [ @@ -159,6 +165,8 @@ exports[`Object metadata update should fail when labelIdentifier is not a TEXT o "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -209,6 +217,8 @@ exports[`Object metadata update should fail when labelIdentifier is not a known "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [ @@ -251,6 +261,8 @@ exports[`Object metadata update should fail when labelIdentifier is not a known "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -318,6 +330,8 @@ exports[`Object metadata update should fail when labelIdentifier is null 1`] = ` "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -338,6 +352,8 @@ exports[`Object metadata update should fail when labelIdentifier is null 1`] = ` "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 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 7f9349e2d5..021a43b742 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 @@ -33,6 +33,8 @@ exports[`Page layout tab creation should fail when pageLayoutId references non-e "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -53,6 +55,8 @@ exports[`Page layout tab creation should fail when pageLayoutId references non-e "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 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 2041bf4bb5..138ba1b5e8 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 @@ -44,6 +44,8 @@ exports[`Page layout widget creation should fail when pageLayoutTabId references "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -64,6 +66,8 @@ exports[`Page layout widget creation should fail when pageLayoutTabId references "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 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 08090310c0..f5b1659fa2 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 @@ -33,6 +33,8 @@ exports[`Role creation should fail when canReadAllObjectRecords is false but can ], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -53,6 +55,8 @@ exports[`Role creation should fail when canReadAllObjectRecords is false but can "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -100,6 +104,8 @@ exports[`Role creation should fail when canReadAllObjectRecords is false but can ], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -120,6 +126,8 @@ exports[`Role creation should fail when canReadAllObjectRecords is false but can "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -167,6 +175,8 @@ exports[`Role creation should fail when canReadAllObjectRecords is false but can ], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -187,6 +197,8 @@ exports[`Role creation should fail when canReadAllObjectRecords is false but can "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -234,6 +246,8 @@ exports[`Role creation should fail when canReadAllObjectRecords is false but mul ], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -254,6 +268,8 @@ exports[`Role creation should fail when canReadAllObjectRecords is false but mul "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -301,6 +317,8 @@ exports[`Role creation should fail when label already exists 1`] = ` ], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -321,6 +339,8 @@ exports[`Role creation should fail when label already exists 1`] = ` "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 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 ecd5cef373..446f1876ef 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 @@ -44,6 +44,8 @@ exports[`Role update should fail updating role with existing write permissions s ], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -64,6 +66,8 @@ exports[`Role update should fail updating role with existing write permissions s "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -110,6 +114,8 @@ exports[`Role update should fail when updating a non-editable system role 1`] = ], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -130,6 +136,8 @@ exports[`Role update should fail when updating a non-editable system role 1`] = "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -176,6 +184,8 @@ exports[`Role update should fail when updating canReadAllObjectRecords to false ], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -196,6 +206,8 @@ exports[`Role update should fail when updating canReadAllObjectRecords to false "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -242,6 +254,8 @@ exports[`Role update should fail when updating canReadAllObjectRecords to false ], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -262,6 +276,8 @@ exports[`Role update should fail when updating canReadAllObjectRecords to false "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -308,6 +324,8 @@ exports[`Role update should fail when updating canReadAllObjectRecords to false ], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -328,6 +346,8 @@ exports[`Role update should fail when updating canReadAllObjectRecords to false "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -374,6 +394,8 @@ exports[`Role update should fail when updating label to one that already exists ], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -394,6 +416,8 @@ exports[`Role update should fail when updating label to one that already exists "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, @@ -440,6 +464,8 @@ exports[`Role update should fail when updating to enable write permissions witho ], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -460,6 +486,8 @@ exports[`Role update should fail when updating to enable write permissions witho "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 0, 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 67d6563066..4373d2bb73 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 @@ -16,6 +16,8 @@ exports[`View Group REST API POST /metadata/viewGroups should fail to create vie "role": [], "roleTarget": [], "routeTrigger": [], + "rowLevelPermissionPredicate": [], + "rowLevelPermissionPredicateGroup": [], "serverlessFunction": [], "view": [], "viewField": [], @@ -53,6 +55,8 @@ exports[`View Group REST API POST /metadata/viewGroups should fail to create vie "invalidRole": 0, "invalidRoleTarget": 0, "invalidRouteTrigger": 0, + "invalidRowLevelPermissionPredicate": 0, + "invalidRowLevelPermissionPredicateGroup": 0, "invalidServerlessFunction": 0, "invalidView": 0, "invalidViewField": 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 8d102244c3..b114d5ffbd 100644 --- a/packages/twenty-shared/src/metadata/all-metadata-name.constant.ts +++ b/packages/twenty-shared/src/metadata/all-metadata-name.constant.ts @@ -4,6 +4,8 @@ export const ALL_METADATA_NAME = { view: 'view', viewField: 'viewField', viewGroup: 'viewGroup', + rowLevelPermissionPredicate: 'rowLevelPermissionPredicate', + rowLevelPermissionPredicateGroup: 'rowLevelPermissionPredicateGroup', index: 'index', serverlessFunction: 'serverlessFunction', cronTrigger: 'cronTrigger',