From 296a3099db2290073a520eee73eec15cffb1af97 Mon Sep 17 00:00:00 2001 From: Weiko Date: Thu, 22 Jan 2026 16:44:18 +0100 Subject: [PATCH] Refactor rls backend simplify add tests (#17357) ## Context Removing full CRUD to RLS since we are actually using an upsert only over the role resolver, this removes a lot of unused code (could be re-added later but unlikely). Simplified the folder arch at the same time Add simple integration tests to RLS --- .../src/generated-metadata/graphql.ts | 103 ---- .../twenty-front/src/generated/graphql.ts | 103 ---- packages/twenty-server/.env.test | 2 +- ...rties-to-compare-and-stringify.constant.ts | 2 +- ...rmission-predicate-group-to-create.util.ts | 62 --- ...ermission-predicate-group-or-throw.util.ts | 45 -- ...ermission-predicate-group-or-throw.util.ts | 42 -- ...predicate-group-to-update-or-throw.util.ts | 55 --- ...cate-group-editable-properties.constant.ts | 0 ...vel-permission-predicate-to-create.util.ts | 74 --- ...evel-permission-predicate-or-throw.util.ts | 45 -- ...evel-permission-predicate-or-throw.util.ts | 42 -- ...-permission-predicate-group-to-dto.util.ts | 0 ...ssion-predicate-to-update-or-throw.util.ts | 55 --- ...-level-permission-predicate-group.input.ts | 23 - ...te-row-level-permission-predicate.input.ts | 42 -- ...-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 | 39 -- ...el-permission-predicate-group.exception.ts | 4 + ...vel-permission-predicate-group.resolver.ts | 82 ---- ...row-level-permission-predicate.resolver.ts | 96 ---- .../row-level-permission.module.ts | 4 - ...evel-permission-predicate-group.service.ts | 242 +--------- .../row-level-permission-predicate.service.ts | 225 +-------- ...ssion-predicate-group-validator.service.ts | 48 ++ ...rmission-predicate-gql-fields.constants.ts | 22 + ...-predicate-upsert.integration-spec.ts.snap | 205 ++++++++ ...ssion-predicate-upsert.integration-spec.ts | 258 ++++++++++ ...ssion-predicate-upsert.integration-spec.ts | 447 ++++++++++++++++++ ...ermission-predicates-query-factory.util.ts | 42 ++ ...rt-row-level-permission-predicates.util.ts | 54 +++ 35 files changed, 1085 insertions(+), 1453 deletions(-) delete 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 delete 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 delete 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 delete 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 rename packages/twenty-server/src/engine/metadata-modules/{flat-row-level-permission-predicate-group => flat-row-level-permission-predicate}/constants/flat-row-level-permission-predicate-group-editable-properties.constant.ts (100%) delete 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 delete 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 delete 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 rename packages/twenty-server/src/engine/metadata-modules/{flat-row-level-permission-predicate-group => flat-row-level-permission-predicate}/utils/from-flat-row-level-permission-predicate-group-to-dto.util.ts (100%) delete 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 delete mode 100644 packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/create-row-level-permission-predicate-group.input.ts delete mode 100644 packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/create-row-level-permission-predicate.input.ts delete mode 100644 packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/delete-row-level-permission-predicate-group.input.ts delete mode 100644 packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/delete-row-level-permission-predicate.input.ts delete mode 100644 packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/destroy-row-level-permission-predicate-group.input.ts delete mode 100644 packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/destroy-row-level-permission-predicate.input.ts delete mode 100644 packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/update-row-level-permission-predicate-group.input.ts delete mode 100644 packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/update-row-level-permission-predicate.input.ts delete mode 100644 packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/resolvers/row-level-permission-predicate-group.resolver.ts delete 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/test/integration/constants/row-level-permission-predicate-gql-fields.constants.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/row-level-permission-predicate/__snapshots__/failing-row-level-permission-predicate-upsert.integration-spec.ts.snap create mode 100644 packages/twenty-server/test/integration/metadata/suites/row-level-permission-predicate/failing-row-level-permission-predicate-upsert.integration-spec.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/row-level-permission-predicate/successful-row-level-permission-predicate-upsert.integration-spec.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/row-level-permission-predicate/utils/upsert-row-level-permission-predicates-query-factory.util.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/row-level-permission-predicate/utils/upsert-row-level-permission-predicates.util.ts diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 07863d4d50..b9c3666b10 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -1061,27 +1061,6 @@ export type CreateRouteTriggerInput = { serverlessFunctionId: Scalars['String']; }; -export type CreateRowLevelPermissionPredicateGroupInput = { - logicalOperator: RowLevelPermissionPredicateGroupLogicalOperator; - objectMetadataId: Scalars['String']; - 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 = { builtHandlerPath?: InputMaybe; code?: InputMaybe; @@ -1278,14 +1257,6 @@ export type DeleteOneObjectInput = { id: Scalars['UUID']; }; -export type DeleteRowLevelPermissionPredicateGroupInput = { - id: Scalars['String']; -}; - -export type DeleteRowLevelPermissionPredicateInput = { - id: Scalars['String']; -}; - export type DeleteSsoInput = { identityProviderId: Scalars['UUID']; }; @@ -2057,8 +2028,6 @@ export type Mutation = { createPageLayoutTab: PageLayoutTab; createPageLayoutWidget: PageLayoutWidget; createPublicDomain: PublicDomain; - createRowLevelPermissionPredicate: RowLevelPermissionPredicate; - createRowLevelPermissionPredicateGroup: RowLevelPermissionPredicateGroup; createSAMLIdentityProvider: SetupSsoOutput; createSkill: Skill; createWebhook: Webhook; @@ -2090,8 +2059,6 @@ export type Mutation = { deleteOneRouteTrigger: RouteTrigger; deleteOneServerlessFunction: ServerlessFunction; deletePublicDomain: Scalars['Boolean']; - deleteRowLevelPermissionPredicate: RowLevelPermissionPredicate; - deleteRowLevelPermissionPredicateGroup: RowLevelPermissionPredicateGroup; deleteSSOIdentityProvider: DeleteSsoOutput; deleteSkill: Skill; deleteTwoFactorAuthenticationMethod: DeleteTwoFactorAuthenticationMethodOutput; @@ -2185,8 +2152,6 @@ export type Mutation = { updatePageLayoutWidget: PageLayoutWidget; updatePageLayoutWithTabsAndWidgets: PageLayout; updatePasswordViaResetToken: InvalidatePasswordOutput; - updateRowLevelPermissionPredicate: RowLevelPermissionPredicate; - updateRowLevelPermissionPredicateGroup: RowLevelPermissionPredicateGroup; updateSkill: Skill; updateUserEmail: Scalars['Boolean']; updateWebhook?: Maybe; @@ -2444,16 +2409,6 @@ export type MutationCreatePublicDomainArgs = { }; -export type MutationCreateRowLevelPermissionPredicateArgs = { - input: CreateRowLevelPermissionPredicateInput; -}; - - -export type MutationCreateRowLevelPermissionPredicateGroupArgs = { - input: CreateRowLevelPermissionPredicateGroupInput; -}; - - export type MutationCreateSamlIdentityProviderArgs = { input: SetupSamlSsoInput; }; @@ -2605,16 +2560,6 @@ export type MutationDeletePublicDomainArgs = { }; -export type MutationDeleteRowLevelPermissionPredicateArgs = { - input: DeleteRowLevelPermissionPredicateInput; -}; - - -export type MutationDeleteRowLevelPermissionPredicateGroupArgs = { - input: DeleteRowLevelPermissionPredicateGroupInput; -}; - - export type MutationDeleteSsoIdentityProviderArgs = { input: DeleteSsoInput; }; @@ -3075,16 +3020,6 @@ export type MutationUpdatePasswordViaResetTokenArgs = { }; -export type MutationUpdateRowLevelPermissionPredicateArgs = { - input: UpdateRowLevelPermissionPredicateInput; -}; - - -export type MutationUpdateRowLevelPermissionPredicateGroupArgs = { - input: UpdateRowLevelPermissionPredicateGroupInput; -}; - - export type MutationUpdateSkillArgs = { input: UpdateSkillInput; }; @@ -3690,9 +3625,6 @@ export type Query = { getQueueJobs: QueueJobsResponse; getQueueMetrics: QueueMetricsData; getRoles: Array; - getRowLevelPermissionPredicate?: Maybe; - getRowLevelPermissionPredicateGroups: Array; - getRowLevelPermissionPredicates: Array; getSSOIdentityProviders: Array; getServerlessFunctionSourceCode?: Maybe; getSystemHealthStatus: SystemHealth; @@ -3962,22 +3894,6 @@ 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; }; @@ -4954,25 +4870,6 @@ export type UpdateRouteTriggerInputUpdates = { path: Scalars['String']; }; -export type UpdateRowLevelPermissionPredicateGroupInput = { - id: Scalars['String']; - logicalOperator?: InputMaybe; - parentRowLevelPermissionPredicateGroupId?: InputMaybe; - positionInRowLevelPermissionPredicateGroup?: InputMaybe; -}; - -export type UpdateRowLevelPermissionPredicateInput = { - fieldMetadataId?: InputMaybe; - 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 f2f6e0085a..137d52c5c8 100644 --- a/packages/twenty-front/src/generated/graphql.ts +++ b/packages/twenty-front/src/generated/graphql.ts @@ -1037,27 +1037,6 @@ export type CreateRouteTriggerInput = { serverlessFunctionId: Scalars['String']; }; -export type CreateRowLevelPermissionPredicateGroupInput = { - logicalOperator: RowLevelPermissionPredicateGroupLogicalOperator; - objectMetadataId: Scalars['String']; - 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 = { builtHandlerPath?: InputMaybe; code?: InputMaybe; @@ -1245,14 +1224,6 @@ export type DeleteOneObjectInput = { id: Scalars['UUID']; }; -export type DeleteRowLevelPermissionPredicateGroupInput = { - id: Scalars['String']; -}; - -export type DeleteRowLevelPermissionPredicateInput = { - id: Scalars['String']; -}; - export type DeleteSsoInput = { identityProviderId: Scalars['UUID']; }; @@ -2020,8 +1991,6 @@ export type Mutation = { createPageLayoutTab: PageLayoutTab; createPageLayoutWidget: PageLayoutWidget; createPublicDomain: PublicDomain; - createRowLevelPermissionPredicate: RowLevelPermissionPredicate; - createRowLevelPermissionPredicateGroup: RowLevelPermissionPredicateGroup; createSAMLIdentityProvider: SetupSsoOutput; createWebhook: Webhook; createWorkflowVersionEdge: WorkflowVersionStepChanges; @@ -2049,8 +2018,6 @@ export type Mutation = { deleteOneRouteTrigger: RouteTrigger; deleteOneServerlessFunction: ServerlessFunction; deletePublicDomain: Scalars['Boolean']; - deleteRowLevelPermissionPredicate: RowLevelPermissionPredicate; - deleteRowLevelPermissionPredicateGroup: RowLevelPermissionPredicateGroup; deleteSSOIdentityProvider: DeleteSsoOutput; deleteTwoFactorAuthenticationMethod: DeleteTwoFactorAuthenticationMethodOutput; deleteUser: User; @@ -2139,8 +2106,6 @@ export type Mutation = { updatePageLayoutWidget: PageLayoutWidget; updatePageLayoutWithTabsAndWidgets: PageLayout; updatePasswordViaResetToken: InvalidatePasswordOutput; - updateRowLevelPermissionPredicate: RowLevelPermissionPredicate; - updateRowLevelPermissionPredicateGroup: RowLevelPermissionPredicateGroup; updateUserEmail: Scalars['Boolean']; updateWebhook?: Maybe; updateWorkflowRunStep: WorkflowAction; @@ -2377,16 +2342,6 @@ export type MutationCreatePublicDomainArgs = { }; -export type MutationCreateRowLevelPermissionPredicateArgs = { - input: CreateRowLevelPermissionPredicateInput; -}; - - -export type MutationCreateRowLevelPermissionPredicateGroupArgs = { - input: CreateRowLevelPermissionPredicateGroupInput; -}; - - export type MutationCreateSamlIdentityProviderArgs = { input: SetupSamlSsoInput; }; @@ -2518,16 +2473,6 @@ export type MutationDeletePublicDomainArgs = { }; -export type MutationDeleteRowLevelPermissionPredicateArgs = { - input: DeleteRowLevelPermissionPredicateInput; -}; - - -export type MutationDeleteRowLevelPermissionPredicateGroupArgs = { - input: DeleteRowLevelPermissionPredicateGroupInput; -}; - - export type MutationDeleteSsoIdentityProviderArgs = { input: DeleteSsoInput; }; @@ -2962,16 +2907,6 @@ export type MutationUpdatePasswordViaResetTokenArgs = { }; -export type MutationUpdateRowLevelPermissionPredicateArgs = { - input: UpdateRowLevelPermissionPredicateInput; -}; - - -export type MutationUpdateRowLevelPermissionPredicateGroupArgs = { - input: UpdateRowLevelPermissionPredicateGroupInput; -}; - - export type MutationUpdateUserEmailArgs = { newEmail: Scalars['String']; verifyEmailRedirectPath?: InputMaybe; @@ -3566,9 +3501,6 @@ export type Query = { getQueueJobs: QueueJobsResponse; getQueueMetrics: QueueMetricsData; getRoles: Array; - getRowLevelPermissionPredicate?: Maybe; - getRowLevelPermissionPredicateGroups: Array; - getRowLevelPermissionPredicates: Array; getSSOIdentityProviders: Array; getServerlessFunctionSourceCode?: Maybe; getSystemHealthStatus: SystemHealth; @@ -3803,22 +3735,6 @@ 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; }; @@ -4739,25 +4655,6 @@ export type UpdateRouteTriggerInputUpdates = { path: Scalars['String']; }; -export type UpdateRowLevelPermissionPredicateGroupInput = { - id: Scalars['String']; - logicalOperator?: InputMaybe; - parentRowLevelPermissionPredicateGroupId?: InputMaybe; - positionInRowLevelPermissionPredicateGroup?: InputMaybe; -}; - -export type UpdateRowLevelPermissionPredicateInput = { - fieldMetadataId?: InputMaybe; - 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-server/.env.test b/packages/twenty-server/.env.test index 78a972934f..9b6993e555 100644 --- a/packages/twenty-server/.env.test +++ b/packages/twenty-server/.env.test @@ -10,7 +10,7 @@ MUTATION_MAXIMUM_RECORD_AFFECTED=100 IS_MULTIWORKSPACE_ENABLED=true FRONTEND_URL=http://localhost:3001 IS_WORKSPACE_CREATION_LIMITED_TO_SERVER_ADMINS=false - +ENTERPRISE_KEY=replace_me_with_a_valid_enterprise_key AUTH_GOOGLE_ENABLED=false MESSAGING_PROVIDER_GMAIL_ENABLED=false 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 1ce439bece..3a3467d6be 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-flat-entity-properties-to-compare-and-stringify.constant.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-flat-entity-properties-to-compare-and-stringify.constant.ts @@ -14,8 +14,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_ROW_LEVEL_PERMISSION_PREDICATE_GROUP_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-row-level-permission-predicate/constants/flat-row-level-permission-predicate-group-editable-properties.constant'; import { FLAT_SKILL_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-skill/constants/flat-skill-editable-properties.constant'; import { FLAT_VIEW_FIELD_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-view-field/constants/flat-view-field-editable-properties.constant'; import { FLAT_VIEW_FILTER_GROUP_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-view-filter-group/constants/flat-view-filter-group-editable-properties.constant'; 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 deleted file mode 100644 index 888c629020..0000000000 --- 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 +++ /dev/null @@ -1,62 +0,0 @@ -/* @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, - workspaceCustomApplicationId, - }: { - createRowLevelPermissionPredicateGroupInput: CreateRowLevelPermissionPredicateGroupInput; - workspaceId: string; - workspaceCustomApplicationId: string; - }): FlatRowLevelPermissionPredicateGroup => { - const sanitizedInput = ( - trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties as unknown as ( - input: CreateRowLevelPermissionPredicateGroupInput, - keys: string[], - ) => CreateRowLevelPermissionPredicateGroupInput - )(rawCreateRowLevelPermissionPredicateGroupInput, [ - 'roleId', - 'objectMetadataId', - 'parentRowLevelPermissionPredicateGroupId', - 'logicalOperator', - ]); - - const { - roleId, - objectMetadataId, - ...createRowLevelPermissionPredicateGroupInput - } = sanitizedInput; - - const createdAt = new Date().toISOString(); - const predicateGroupId = v4(); - - return { - id: predicateGroupId, - workspaceId, - roleId, - objectMetadataId, - childRowLevelPermissionPredicateGroupIds: [], - rowLevelPermissionPredicateIds: [], - createdAt, - updatedAt: createdAt, - deletedAt: null, - universalIdentifier: predicateGroupId, - parentRowLevelPermissionPredicateGroupId: - createRowLevelPermissionPredicateGroupInput.parentRowLevelPermissionPredicateGroupId ?? - null, - logicalOperator: - createRowLevelPermissionPredicateGroupInput.logicalOperator, - positionInRowLevelPermissionPredicateGroup: - createRowLevelPermissionPredicateGroupInput.positionInRowLevelPermissionPredicateGroup ?? - null, - applicationId: workspaceCustomApplicationId, - }; - }; 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 deleted file mode 100644 index d2c66d2c3e..0000000000 --- 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 +++ /dev/null @@ -1,45 +0,0 @@ -/* @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 deleted file mode 100644 index 6d0f09a32a..0000000000 --- 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 +++ /dev/null @@ -1,42 +0,0 @@ -/* @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-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 deleted file mode 100644 index 0ce279fecd..0000000000 --- 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 +++ /dev/null @@ -1,55 +0,0 @@ -/* @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-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/constants/flat-row-level-permission-predicate-group-editable-properties.constant.ts similarity index 100% rename from packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate-group/constants/flat-row-level-permission-predicate-group-editable-properties.constant.ts rename to packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/constants/flat-row-level-permission-predicate-group-editable-properties.constant.ts 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 deleted file mode 100644 index c608486b47..0000000000 --- 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 +++ /dev/null @@ -1,74 +0,0 @@ -/* @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, - workspaceCustomApplicationId, - }: { - createRowLevelPermissionPredicateInput: CreateRowLevelPermissionPredicateInput; - workspaceId: string; - workspaceCustomApplicationId: 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: workspaceCustomApplicationId, - }; - }; 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 deleted file mode 100644 index 0e742492ca..0000000000 --- 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 +++ /dev/null @@ -1,45 +0,0 @@ -/* @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 deleted file mode 100644 index 07ec70f58a..0000000000 --- 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 +++ /dev/null @@ -1,42 +0,0 @@ -/* @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-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/utils/from-flat-row-level-permission-predicate-group-to-dto.util.ts similarity index 100% rename from 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 rename to packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-flat-row-level-permission-predicate-group-to-dto.util.ts 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 deleted file mode 100644 index a9ef2ff505..0000000000 --- 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 +++ /dev/null @@ -1,55 +0,0 @@ -/* @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 deleted file mode 100644 index 35ed6fae0e..0000000000 --- a/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/create-row-level-permission-predicate-group.input.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* @license Enterprise */ - -import { Field, InputType } from '@nestjs/graphql'; - -import { RowLevelPermissionPredicateGroupLogicalOperator } from 'twenty-shared/types'; - -@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; - - @Field(() => String) - objectMetadataId: 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 deleted file mode 100644 index dba5587bc4..0000000000 --- a/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/create-row-level-permission-predicate.input.ts +++ /dev/null @@ -1,42 +0,0 @@ -/* @license Enterprise */ - -import { Field, InputType } from '@nestjs/graphql'; - -import GraphQLJSON from 'graphql-type-json'; -import { - RowLevelPermissionPredicateOperand, - RowLevelPermissionPredicateValue, -} from 'twenty-shared/types'; - -@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 deleted file mode 100644 index 5bf5714005..0000000000 --- a/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/delete-row-level-permission-predicate-group.input.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* @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 deleted file mode 100644 index fc3a71e7f7..0000000000 --- a/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/delete-row-level-permission-predicate.input.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* @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 deleted file mode 100644 index 14dc103d97..0000000000 --- a/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/destroy-row-level-permission-predicate-group.input.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* @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 deleted file mode 100644 index bbd36ff993..0000000000 --- a/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/destroy-row-level-permission-predicate.input.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* @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 deleted file mode 100644 index c4f3d01b15..0000000000 --- a/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/update-row-level-permission-predicate-group.input.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* @license Enterprise */ - -import { Field, InputType } from '@nestjs/graphql'; - -import { RowLevelPermissionPredicateGroupLogicalOperator } from 'twenty-shared/types'; - -@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 deleted file mode 100644 index 471457d77b..0000000000 --- a/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/update-row-level-permission-predicate.input.ts +++ /dev/null @@ -1,39 +0,0 @@ -/* @license Enterprise */ - -import { Field, InputType } from '@nestjs/graphql'; - -import GraphQLJSON from 'graphql-type-json'; -import { - RowLevelPermissionPredicateOperand, - RowLevelPermissionPredicateValue, -} from 'twenty-shared/types'; - -@InputType() -export class UpdateRowLevelPermissionPredicateInput { - @Field(() => String) - id: string; - - @Field(() => String, { nullable: true }) - fieldMetadataId?: 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/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 index 3dc4636ebb..7fda553501 100644 --- 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 @@ -15,7 +15,9 @@ export const RowLevelPermissionPredicateGroupExceptionCode = INVALID_ROW_LEVEL_PERMISSION_PREDICATE_GROUP_DATA: 'INVALID_ROW_LEVEL_PERMISSION_PREDICATE_GROUP_DATA', ROLE_NOT_FOUND: 'ROLE_NOT_FOUND', + OBJECT_METADATA_NOT_FOUND: 'OBJECT_METADATA_NOT_FOUND', UNAUTHORIZED_ROLE_MODIFICATION: 'UNAUTHORIZED_ROLE_MODIFICATION', + UNAUTHORIZED_OBJECT_MODIFICATION: 'UNAUTHORIZED_OBJECT_MODIFICATION', ROW_LEVEL_PERMISSION_FEATURE_DISABLED: 'ROW_LEVEL_PERMISSION_FEATURE_DISABLED', } as const); @@ -27,7 +29,9 @@ const rowLevelPermissionPredicateGroupExceptionUserFriendlyMessages: Record< 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.`, + OBJECT_METADATA_NOT_FOUND: msg`Object metadata not found.`, UNAUTHORIZED_ROLE_MODIFICATION: msg`Cannot modify predicate group belonging to a different role.`, + UNAUTHORIZED_OBJECT_MODIFICATION: msg`Cannot modify predicate group belonging to a different object.`, ROW_LEVEL_PERMISSION_FEATURE_DISABLED: msg`Row level permission predicate feature is disabled.`, INTERNAL_SERVER_ERROR: msg`An unexpected error occurred.`, }; 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 deleted file mode 100644 index 8c37326d13..0000000000 --- a/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/resolvers/row-level-permission-predicate-group.resolver.ts +++ /dev/null @@ -1,82 +0,0 @@ -/* @license Enterprise */ - -import { UseGuards } from '@nestjs/common'; -import { Args, Mutation, Query, Resolver } from '@nestjs/graphql'; - -import { RowLevelPermissionPredicateGroupLogicalOperator } from 'twenty-shared/types'; - -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 { 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 deleted file mode 100644 index c4744896a3..0000000000 --- a/packages/twenty-server/src/engine/metadata-modules/row-level-permission-predicate/resolvers/row-level-permission-predicate.resolver.ts +++ /dev/null @@ -1,96 +0,0 @@ -/* @license Enterprise */ - -import { UseGuards } from '@nestjs/common'; -import { Args, Mutation, Query, Resolver } from '@nestjs/graphql'; - -import { RowLevelPermissionPredicateOperand } from 'twenty-shared/types'; - -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 { 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 index b53112bfb0..aa576004bb 100644 --- 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 @@ -8,8 +8,6 @@ import { BillingModule } from 'src/engine/core-modules/billing/billing.module'; 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'; @@ -30,8 +28,6 @@ import { WorkspaceMigrationModule } from 'src/engine/workspace-manager/workspace providers: [ RowLevelPermissionPredicateService, RowLevelPermissionPredicateGroupService, - RowLevelPermissionPredicateResolver, - RowLevelPermissionPredicateGroupResolver, ], exports: [ RowLevelPermissionPredicateService, 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 index b2e6a29ea9..6c63955441 100644 --- 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 @@ -7,215 +7,26 @@ import { InjectRepository } from '@nestjs/typeorm'; import { isDefined } from 'twenty-shared/utils'; import { Repository } from 'typeorm'; -import { ApplicationService } from 'src/engine/core-modules/application/application.service'; import { BillingEntitlementKey } from 'src/engine/core-modules/billing/enums/billing-entitlement-key.enum'; import { BillingService } from 'src/engine/core-modules/billing/services/billing.service'; import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service'; -import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util'; import { 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 { fromFlatRowLevelPermissionPredicateGroupToDto } from 'src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-flat-row-level-permission-predicate-group-to-dto.util'; import { RowLevelPermissionPredicateGroupDTO } from 'src/engine/metadata-modules/row-level-permission-predicate/dtos/row-level-permission-predicate-group.dto'; import { RowLevelPermissionPredicateGroupEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate-group.entity'; -import { - RowLevelPermissionPredicateGroupException, - 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 { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service'; -import { WorkspaceMigrationBuilderException } from 'src/engine/workspace-manager/workspace-migration/exceptions/workspace-migration-builder-exception'; -import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration/services/workspace-migration-validate-build-and-run-service'; @Injectable() export class RowLevelPermissionPredicateGroupService { constructor( - private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService, private readonly flatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService, private readonly workspaceCacheService: WorkspaceCacheService, private readonly billingService: BillingService, @InjectRepository(RowLevelPermissionPredicateGroupEntity) private readonly rowLevelPermissionPredicateGroupRepository: Repository, private readonly configService: ConfigService, - private readonly applicationService: ApplicationService, ) {} - async createOne({ - createRowLevelPermissionPredicateGroupInput, - workspaceId, - }: { - createRowLevelPermissionPredicateGroupInput: CreateRowLevelPermissionPredicateGroupInput; - workspaceId: string; - }): Promise { - await this.hasRowLevelPermissionFeatureOrThrow(workspaceId); - const { workspaceCustomFlatApplication } = - await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow( - { - workspaceId, - }, - ); - - const flatGroupToCreate = - fromCreateRowLevelPermissionPredicateGroupInputToFlatRowLevelPermissionPredicateGroupToCreate( - { - createRowLevelPermissionPredicateGroupInput, - workspaceId, - workspaceCustomApplicationId: workspaceCustomFlatApplication.id, - }, - ); - - 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 { - await this.hasRowLevelPermissionFeatureOrThrow(workspaceId); - - 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 { - await this.hasRowLevelPermissionFeatureOrThrow(workspaceId); - - 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 { - await this.hasRowLevelPermissionFeatureOrThrow(workspaceId); - - 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 { @@ -318,45 +129,6 @@ export class RowLevelPermissionPredicateGroupService { ]); } - 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 WorkspaceMigrationBuilderException( - validateAndBuildResult, - 'Validation errors occurred while applying row level permission predicate group mutation', - ); - } - - await this.workspaceCacheService.invalidateAndRecompute(workspaceId, [ - 'rolesPermissions', - 'flatRowLevelPermissionPredicateMaps', - ]); - } - private async hasRowLevelPermissionFeature( workspaceId: string, ): Promise { @@ -372,16 +144,4 @@ export class RowLevelPermissionPredicateGroupService { return hasValidEnterpriseKey && isRowLevelPermissionEnabled; } - - private async hasRowLevelPermissionFeatureOrThrow(workspaceId: string) { - const hasRowLevelPermissionFeature = - await this.hasRowLevelPermissionFeature(workspaceId); - - if (!hasRowLevelPermissionFeature) { - throw new RowLevelPermissionPredicateGroupException( - 'Row level permission predicate feature is disabled', - RowLevelPermissionPredicateGroupExceptionCode.ROW_LEVEL_PERMISSION_FEATURE_DISABLED, - ); - } - } } 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 index 3005558d95..8c4c621de3 100644 --- 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 @@ -11,18 +11,9 @@ import { BillingEntitlementKey } from 'src/engine/core-modules/billing/enums/bil import { BillingService } from 'src/engine/core-modules/billing/services/billing.service'; import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service'; import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/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 { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.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 { 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 { fromFlatRowLevelPermissionPredicateGroupToDto } from 'src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-flat-row-level-permission-predicate-group-to-dto.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 { type RowLevelPermissionPredicateGroupInput, type RowLevelPermissionPredicateInput, @@ -51,176 +42,6 @@ export class RowLevelPermissionPredicateService { private readonly applicationService: ApplicationService, ) {} - async createOne({ - createRowLevelPermissionPredicateInput, - workspaceId, - }: { - createRowLevelPermissionPredicateInput: CreateRowLevelPermissionPredicateInput; - workspaceId: string; - }): Promise { - await this.hasRowLevelPermissionFeatureOrThrow(workspaceId); - - const { workspaceCustomFlatApplication } = - await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow( - { - workspaceId, - }, - ); - const flatPredicateToCreate = - fromCreateRowLevelPermissionPredicateInputToFlatRowLevelPermissionPredicateToCreate( - { - createRowLevelPermissionPredicateInput, - workspaceId, - workspaceCustomApplicationId: workspaceCustomFlatApplication.id, - }, - ); - - 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 { - await this.hasRowLevelPermissionFeatureOrThrow(workspaceId); - - 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 { - await this.hasRowLevelPermissionFeatureOrThrow(workspaceId); - - 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 { - await this.hasRowLevelPermissionFeatureOrThrow(workspaceId); - - 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 { @@ -378,7 +199,6 @@ export class RowLevelPermissionPredicateService { existingGroups, inputGroups: predicateGroups, roleId, - objectMetadataId, workspaceId, flatRowLevelPermissionPredicateGroupMaps, workspaceCustomApplicationId: workspaceCustomFlatApplication.id, @@ -449,7 +269,6 @@ export class RowLevelPermissionPredicateService { existingGroups, inputGroups, roleId, - objectMetadataId, workspaceId, flatRowLevelPermissionPredicateGroupMaps, workspaceCustomApplicationId, @@ -457,7 +276,6 @@ export class RowLevelPermissionPredicateService { existingGroups: FlatRowLevelPermissionPredicateGroup[]; inputGroups: RowLevelPermissionPredicateGroupInput[]; roleId: string; - objectMetadataId: string; workspaceId: string; flatRowLevelPermissionPredicateGroupMaps: FlatEntityMaps; workspaceCustomApplicationId: string; @@ -495,7 +313,7 @@ export class RowLevelPermissionPredicateService { id: groupId, workspaceId, roleId, - objectMetadataId, + objectMetadataId: inputGroup.objectMetadataId, logicalOperator: inputGroup.logicalOperator, parentRowLevelPermissionPredicateGroupId: inputGroup.parentRowLevelPermissionPredicateGroupId ?? null, @@ -676,45 +494,6 @@ export class RowLevelPermissionPredicateService { ]); } - 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 WorkspaceMigrationBuilderException( - validateAndBuildResult, - 'Validation errors occurred while applying row level permission predicate mutation', - ); - } - - await this.workspaceCacheService.invalidateAndRecompute(workspaceId, [ - 'rolesPermissions', - 'flatRowLevelPermissionPredicateGroupMaps', - ]); - } - private async hasRowLevelPermissionFeature( workspaceId: string, ): Promise { diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-row-level-permission-predicate-group-validator.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-row-level-permission-predicate-group-validator.service.ts index e10142d2d6..e13741781e 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-row-level-permission-predicate-group-validator.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-row-level-permission-predicate-group-validator.service.ts @@ -26,9 +26,11 @@ export class FlatRowLevelPermissionPredicateGroupValidatorService { flatRowLevelPermissionPredicateGroupMaps: optimisticFlatPredicateGroupMaps, flatRoleMaps, + flatObjectMetadataMaps, } = optimisticFlatEntityMapsAndRelatedFlatEntityMaps as Partial<{ flatRowLevelPermissionPredicateGroupMaps: typeof optimisticFlatEntityMapsAndRelatedFlatEntityMaps.flatRowLevelPermissionPredicateGroupMaps; flatRoleMaps: typeof optimisticFlatEntityMapsAndRelatedFlatEntityMaps.flatRoleMaps; + flatObjectMetadataMaps: typeof optimisticFlatEntityMapsAndRelatedFlatEntityMaps.flatObjectMetadataMaps; }>; const validationResult = getEmptyFlatEntityValidationError({ flatEntityMinimalInformation: { @@ -86,6 +88,21 @@ export class FlatRowLevelPermissionPredicateGroupValidatorService { }); } + const objectMetadata = flatObjectMetadataMaps + ? findFlatEntityByIdInFlatEntityMaps({ + flatEntityId: flatPredicateGroupToValidate.objectMetadataId, + flatEntityMaps: flatObjectMetadataMaps, + }) + : undefined; + + if (!isDefined(objectMetadata)) { + validationResult.errors.push({ + code: RowLevelPermissionPredicateGroupExceptionCode.OBJECT_METADATA_NOT_FOUND, + message: t`Object metadata not found`, + userFriendlyMessage: msg`Object metadata not found`, + }); + } + return validationResult; } @@ -135,9 +152,11 @@ export class FlatRowLevelPermissionPredicateGroupValidatorService { flatRowLevelPermissionPredicateGroupMaps: optimisticFlatPredicateGroupMaps, flatRoleMaps, + flatObjectMetadataMaps, } = optimisticFlatEntityMapsAndRelatedFlatEntityMaps as Partial<{ flatRowLevelPermissionPredicateGroupMaps: typeof optimisticFlatEntityMapsAndRelatedFlatEntityMaps.flatRowLevelPermissionPredicateGroupMaps; flatRoleMaps: typeof optimisticFlatEntityMapsAndRelatedFlatEntityMaps.flatRoleMaps; + flatObjectMetadataMaps: typeof optimisticFlatEntityMapsAndRelatedFlatEntityMaps.flatObjectMetadataMaps; }>; const existingPredicateGroup = @@ -180,6 +199,20 @@ export class FlatRowLevelPermissionPredicateGroupValidatorService { }); } + if ( + updatedPredicateGroup.objectMetadataId !== + existingPredicateGroup.objectMetadataId + ) { + const existingObjectMetadataId = existingPredicateGroup.objectMetadataId; + const updatedObjectMetadataId = updatedPredicateGroup.objectMetadataId; + + validationResult.errors.push({ + code: RowLevelPermissionPredicateGroupExceptionCode.UNAUTHORIZED_OBJECT_MODIFICATION, + message: t`Cannot modify predicate group to change its object from ${existingObjectMetadataId} to ${updatedObjectMetadataId}`, + userFriendlyMessage: msg`Cannot modify predicate group to change its object`, + }); + } + if ( isDefined( updatedPredicateGroup.parentRowLevelPermissionPredicateGroupId, @@ -216,6 +249,21 @@ export class FlatRowLevelPermissionPredicateGroupValidatorService { }); } + const objectMetadata = flatObjectMetadataMaps + ? findFlatEntityByIdInFlatEntityMaps({ + flatEntityId: updatedPredicateGroup.objectMetadataId, + flatEntityMaps: flatObjectMetadataMaps, + }) + : undefined; + + if (!isDefined(objectMetadata)) { + validationResult.errors.push({ + code: RowLevelPermissionPredicateGroupExceptionCode.OBJECT_METADATA_NOT_FOUND, + message: t`Object metadata not found`, + userFriendlyMessage: msg`Object metadata not found`, + }); + } + return validationResult; } } diff --git a/packages/twenty-server/test/integration/constants/row-level-permission-predicate-gql-fields.constants.ts b/packages/twenty-server/test/integration/constants/row-level-permission-predicate-gql-fields.constants.ts new file mode 100644 index 0000000000..41ea4d6899 --- /dev/null +++ b/packages/twenty-server/test/integration/constants/row-level-permission-predicate-gql-fields.constants.ts @@ -0,0 +1,22 @@ +export const ROW_LEVEL_PERMISSION_PREDICATE_GQL_FIELDS = ` + id + fieldMetadataId + objectMetadataId + operand + value + subFieldName + workspaceMemberFieldMetadataId + workspaceMemberSubFieldName + rowLevelPermissionPredicateGroupId + positionInRowLevelPermissionPredicateGroup + roleId +`; + +export const ROW_LEVEL_PERMISSION_PREDICATE_GROUP_GQL_FIELDS = ` + id + parentRowLevelPermissionPredicateGroupId + logicalOperator + positionInRowLevelPermissionPredicateGroup + roleId + objectMetadataId +`; diff --git a/packages/twenty-server/test/integration/metadata/suites/row-level-permission-predicate/__snapshots__/failing-row-level-permission-predicate-upsert.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/row-level-permission-predicate/__snapshots__/failing-row-level-permission-predicate-upsert.integration-spec.ts.snap new file mode 100644 index 0000000000..d87168e31c --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/row-level-permission-predicate/__snapshots__/failing-row-level-permission-predicate-upsert.integration-spec.ts.snap @@ -0,0 +1,205 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`Row Level Permission Predicate upsert should fail when fieldMetadataId in predicate does not exist 1`] = ` +{ + "extensions": { + "code": "METADATA_VALIDATION_FAILED", + "errors": { + "rowLevelPermissionPredicate": [ + { + "errors": [ + { + "code": "FIELD_METADATA_NOT_FOUND", + "message": "Field metadata not found", + "userFriendlyMessage": "Field metadata not found", + }, + ], + "flatEntityMinimalInformation": { + "id": Any, + "universalIdentifier": Any, + }, + "metadataName": "rowLevelPermissionPredicate", + "status": "fail", + "type": "create", + }, + ], + }, + "message": "Validation failed for 1 rowLevelPermissionPredicate", + "summary": { + "rowLevelPermissionPredicate": 1, + "totalErrors": 1, + }, + "userFriendlyMessage": "Metadata validation failed", + }, + "message": "Validation errors occurred while upserting row level permission predicates", + "name": "GraphQLError", +} +`; + +exports[`Row Level Permission Predicate upsert should fail when fieldMetadataId in predicate is not a valid UUID 1`] = ` +{ + "extensions": { + "code": "GRAPHQL_VALIDATION_FAILED", + "http": { + "status": 400, + }, + "userFriendlyMessage": "An error occurred.", + "value": "invalid-uuid", + }, + "message": "Invalid UUID", + "name": "ValidationError", +} +`; + +exports[`Row Level Permission Predicate upsert should fail when objectMetadataId in predicate group does not exist 1`] = ` +{ + "extensions": { + "code": "METADATA_VALIDATION_FAILED", + "errors": { + "rowLevelPermissionPredicateGroup": [ + { + "errors": [ + { + "code": "OBJECT_METADATA_NOT_FOUND", + "message": "Object metadata not found", + "userFriendlyMessage": "Object metadata not found", + }, + ], + "flatEntityMinimalInformation": { + "id": Any, + "universalIdentifier": Any, + }, + "metadataName": "rowLevelPermissionPredicateGroup", + "status": "fail", + "type": "create", + }, + ], + }, + "message": "Validation failed for 1 rowLevelPermissionPredicateGroup", + "summary": { + "rowLevelPermissionPredicateGroup": 1, + "totalErrors": 1, + }, + "userFriendlyMessage": "Metadata validation failed", + }, + "message": "Validation errors occurred while upserting row level permission predicates", + "name": "GraphQLError", +} +`; + +exports[`Row Level Permission Predicate upsert should fail when objectMetadataId in predicate group is not a valid UUID 1`] = ` +{ + "extensions": { + "code": "GRAPHQL_VALIDATION_FAILED", + "http": { + "status": 400, + }, + "userFriendlyMessage": "An error occurred.", + "value": "invalid-uuid", + }, + "message": "Invalid UUID", + "name": "ValidationError", +} +`; + +exports[`Row Level Permission Predicate upsert should fail when objectMetadataId is not a valid UUID 1`] = ` +{ + "extensions": { + "code": "GRAPHQL_VALIDATION_FAILED", + "http": { + "status": 400, + }, + "userFriendlyMessage": "An error occurred.", + "value": "invalid-uuid", + }, + "message": "Invalid UUID", + "name": "ValidationError", +} +`; + +exports[`Row Level Permission Predicate upsert should fail when parentRowLevelPermissionPredicateGroupId in group does not exist 1`] = ` +{ + "extensions": { + "code": "METADATA_VALIDATION_FAILED", + "errors": { + "rowLevelPermissionPredicateGroup": [ + { + "errors": [ + { + "code": "INVALID_ROW_LEVEL_PERMISSION_PREDICATE_GROUP_DATA", + "message": "Parent row level permission predicate group not found", + "userFriendlyMessage": "Parent row level permission predicate group not found", + }, + ], + "flatEntityMinimalInformation": { + "id": Any, + "universalIdentifier": Any, + }, + "metadataName": "rowLevelPermissionPredicateGroup", + "status": "fail", + "type": "create", + }, + ], + }, + "message": "Validation failed for 1 rowLevelPermissionPredicateGroup", + "summary": { + "rowLevelPermissionPredicateGroup": 1, + "totalErrors": 1, + }, + "userFriendlyMessage": "Metadata validation failed", + }, + "message": "Validation errors occurred while upserting row level permission predicates", + "name": "GraphQLError", +} +`; + +exports[`Row Level Permission Predicate upsert should fail when roleId is not a valid UUID 1`] = ` +{ + "extensions": { + "code": "GRAPHQL_VALIDATION_FAILED", + "http": { + "status": 400, + }, + "userFriendlyMessage": "An error occurred.", + "value": "invalid-uuid", + }, + "message": "Invalid UUID", + "name": "ValidationError", +} +`; + +exports[`Row Level Permission Predicate upsert should fail when rowLevelPermissionPredicateGroupId in predicate does not exist 1`] = ` +{ + "extensions": { + "code": "METADATA_VALIDATION_FAILED", + "errors": { + "rowLevelPermissionPredicate": [ + { + "errors": [ + { + "code": "INVALID_ROW_LEVEL_PERMISSION_PREDICATE_DATA", + "message": "Row level permission predicate group not found", + "userFriendlyMessage": "Row level permission predicate group not found", + }, + ], + "flatEntityMinimalInformation": { + "id": Any, + "universalIdentifier": Any, + }, + "metadataName": "rowLevelPermissionPredicate", + "status": "fail", + "type": "create", + }, + ], + }, + "message": "Validation failed for 1 rowLevelPermissionPredicate", + "summary": { + "rowLevelPermissionPredicate": 1, + "totalErrors": 1, + }, + "userFriendlyMessage": "Metadata validation failed", + }, + "message": "Validation errors occurred while upserting row level permission predicates", + "name": "GraphQLError", +} +`; diff --git a/packages/twenty-server/test/integration/metadata/suites/row-level-permission-predicate/failing-row-level-permission-predicate-upsert.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/row-level-permission-predicate/failing-row-level-permission-predicate-upsert.integration-spec.ts new file mode 100644 index 0000000000..accb8cc049 --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/row-level-permission-predicate/failing-row-level-permission-predicate-upsert.integration-spec.ts @@ -0,0 +1,258 @@ +import { expectOneNotInternalServerErrorSnapshot } from 'test/integration/graphql/utils/expect-one-not-internal-server-error-snapshot.util'; +import { findManyObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/find-many-object-metadata.util'; +import { createOneRole } from 'test/integration/metadata/suites/role/utils/create-one-role.util'; +import { deleteOneRole } from 'test/integration/metadata/suites/role/utils/delete-one-role.util'; +import { upsertRowLevelPermissionPredicates } from 'test/integration/metadata/suites/row-level-permission-predicate/utils/upsert-row-level-permission-predicates.util'; +import { updateFeatureFlag } from 'test/integration/metadata/suites/utils/update-feature-flag.util'; +import { jestExpectToBeDefined } from 'test/utils/jest-expect-to-be-defined.util.test'; +import { + eachTestingContextFilter, + type EachTestingContext, +} from 'twenty-shared/testing'; +import { + RowLevelPermissionPredicateGroupLogicalOperator, + RowLevelPermissionPredicateOperand, +} from 'twenty-shared/types'; +import { v4 } from 'uuid'; + +import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum'; +import { type UpsertRowLevelPermissionPredicatesInput } from 'src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/upsert-row-level-permission-predicates.input'; + +type TestSetup = { + createdRoleId: string; + companyObjectMetadataId: string; + companyNameFieldMetadataId: string; +}; + +type TestContext = { + input: (testSetup: TestSetup) => UpsertRowLevelPermissionPredicatesInput; +}; + +const failingRowLevelPermissionPredicateUpsertTestCases: EachTestingContext[] = + [ + { + title: 'when roleId is not a valid UUID', + context: { + input: (testSetup) => ({ + roleId: 'invalid-uuid', + objectMetadataId: testSetup.companyObjectMetadataId, + predicates: [], + predicateGroups: [], + }), + }, + }, + { + title: 'when objectMetadataId is not a valid UUID', + context: { + input: (testSetup) => ({ + roleId: testSetup.createdRoleId, + objectMetadataId: 'invalid-uuid', + predicates: [], + predicateGroups: [], + }), + }, + }, + { + title: 'when fieldMetadataId in predicate is not a valid UUID', + context: { + input: (testSetup) => ({ + roleId: testSetup.createdRoleId, + objectMetadataId: testSetup.companyObjectMetadataId, + predicates: [ + { + fieldMetadataId: 'invalid-uuid', + operand: RowLevelPermissionPredicateOperand.CONTAINS, + }, + ], + predicateGroups: [], + }), + }, + }, + { + title: 'when fieldMetadataId in predicate does not exist', + context: { + input: (testSetup) => ({ + roleId: testSetup.createdRoleId, + objectMetadataId: testSetup.companyObjectMetadataId, + predicates: [ + { + fieldMetadataId: v4(), + operand: RowLevelPermissionPredicateOperand.CONTAINS, + }, + ], + predicateGroups: [], + }), + }, + }, + { + title: + 'when rowLevelPermissionPredicateGroupId in predicate does not exist', + context: { + input: (testSetup) => ({ + roleId: testSetup.createdRoleId, + objectMetadataId: testSetup.companyObjectMetadataId, + predicates: [ + { + fieldMetadataId: testSetup.companyNameFieldMetadataId, + operand: RowLevelPermissionPredicateOperand.CONTAINS, + rowLevelPermissionPredicateGroupId: v4(), + }, + ], + predicateGroups: [], + }), + }, + }, + { + title: + 'when parentRowLevelPermissionPredicateGroupId in group does not exist', + context: { + input: (testSetup) => ({ + roleId: testSetup.createdRoleId, + objectMetadataId: testSetup.companyObjectMetadataId, + predicates: [], + predicateGroups: [ + { + objectMetadataId: testSetup.companyObjectMetadataId, + logicalOperator: + RowLevelPermissionPredicateGroupLogicalOperator.AND, + parentRowLevelPermissionPredicateGroupId: v4(), + }, + ], + }), + }, + }, + { + title: 'when objectMetadataId in predicate group is not a valid UUID', + context: { + input: (testSetup) => ({ + roleId: testSetup.createdRoleId, + objectMetadataId: testSetup.companyObjectMetadataId, + predicates: [], + predicateGroups: [ + { + objectMetadataId: 'invalid-uuid', + logicalOperator: + RowLevelPermissionPredicateGroupLogicalOperator.AND, + }, + ], + }), + }, + }, + { + title: 'when objectMetadataId in predicate group does not exist', + context: { + input: (testSetup) => ({ + roleId: testSetup.createdRoleId, + objectMetadataId: testSetup.companyObjectMetadataId, + predicates: [], + predicateGroups: [ + { + objectMetadataId: v4(), + logicalOperator: + RowLevelPermissionPredicateGroupLogicalOperator.AND, + }, + ], + }), + }, + }, + ]; + +describe('Row Level Permission Predicate upsert should fail', () => { + let createdRoleId: string; + let companyObjectMetadataId: string; + let companyNameFieldMetadataId: string; + + beforeAll(async () => { + await updateFeatureFlag({ + featureFlag: FeatureFlagKey.IS_ROW_LEVEL_PERMISSION_PREDICATES_ENABLED, + value: true, + expectToFail: false, + }); + + const { objects } = await findManyObjectMetadata({ + expectToFail: false, + input: { + filter: {}, + paging: { first: 1000 }, + }, + gqlFields: ` + id + nameSingular + fieldsList { + id + name + } + `, + }); + + jestExpectToBeDefined(objects); + + const companyObjectMetadata = objects.find( + (object: { nameSingular: string }) => object.nameSingular === 'company', + ); + + jestExpectToBeDefined(companyObjectMetadata); + companyObjectMetadataId = companyObjectMetadata.id; + + jestExpectToBeDefined(companyObjectMetadata.fieldsList); + const nameField = companyObjectMetadata.fieldsList.find( + (field: { name: string }) => field.name === 'name', + ); + + jestExpectToBeDefined(nameField); + companyNameFieldMetadataId = nameField.id; + + const { data: roleData } = await createOneRole({ + expectToFail: false, + input: { + label: 'Test Role For Failing RLS Predicates', + description: 'A role for failing RLS predicate testing', + icon: 'IconSettings', + canUpdateAllSettings: false, + canAccessAllTools: true, + canReadAllObjectRecords: true, + canUpdateAllObjectRecords: false, + canSoftDeleteAllObjectRecords: false, + canDestroyAllObjectRecords: false, + canBeAssignedToUsers: true, + canBeAssignedToAgents: false, + canBeAssignedToApiKeys: false, + }, + }); + + createdRoleId = roleData?.createOneRole?.id; + jestExpectToBeDefined(createdRoleId); + }); + + afterAll(async () => { + await updateFeatureFlag({ + featureFlag: FeatureFlagKey.IS_ROW_LEVEL_PERMISSION_PREDICATES_ENABLED, + value: false, + expectToFail: false, + }); + + if (createdRoleId) { + await deleteOneRole({ + expectToFail: false, + input: { idToDelete: createdRoleId }, + }); + } + }); + + it.each( + eachTestingContextFilter(failingRowLevelPermissionPredicateUpsertTestCases), + )('$title', async ({ context }) => { + const { errors } = await upsertRowLevelPermissionPredicates({ + expectToFail: true, + input: context.input({ + createdRoleId, + companyObjectMetadataId, + companyNameFieldMetadataId, + }), + }); + + expectOneNotInternalServerErrorSnapshot({ + errors, + }); + }); +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/row-level-permission-predicate/successful-row-level-permission-predicate-upsert.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/row-level-permission-predicate/successful-row-level-permission-predicate-upsert.integration-spec.ts new file mode 100644 index 0000000000..237e42176e --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/row-level-permission-predicate/successful-row-level-permission-predicate-upsert.integration-spec.ts @@ -0,0 +1,447 @@ +import { findManyObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/find-many-object-metadata.util'; +import { createOneRole } from 'test/integration/metadata/suites/role/utils/create-one-role.util'; +import { deleteOneRole } from 'test/integration/metadata/suites/role/utils/delete-one-role.util'; +import { upsertRowLevelPermissionPredicates } from 'test/integration/metadata/suites/row-level-permission-predicate/utils/upsert-row-level-permission-predicates.util'; +import { updateFeatureFlag } from 'test/integration/metadata/suites/utils/update-feature-flag.util'; +import { jestExpectToBeDefined } from 'test/utils/jest-expect-to-be-defined.util.test'; +import { + RowLevelPermissionPredicateGroupLogicalOperator, + RowLevelPermissionPredicateOperand, +} from 'twenty-shared/types'; + +import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum'; +import { type UpsertRowLevelPermissionPredicatesInput } from 'src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/upsert-row-level-permission-predicates.input'; + +describe('Row Level Permission Predicate upsert should succeed', () => { + let companyObjectMetadataId: string; + let companyNameFieldMetadataId: string; + let createdRoleId: string; + + beforeAll(async () => { + await updateFeatureFlag({ + featureFlag: FeatureFlagKey.IS_ROW_LEVEL_PERMISSION_PREDICATES_ENABLED, + value: true, + expectToFail: false, + }); + + const { objects } = await findManyObjectMetadata({ + expectToFail: false, + input: { + filter: {}, + paging: { first: 1000 }, + }, + gqlFields: ` + id + nameSingular + fieldsList { + id + name + } + `, + }); + + jestExpectToBeDefined(objects); + + const companyObjectMetadata = objects.find( + (object: { nameSingular: string }) => object.nameSingular === 'company', + ); + + jestExpectToBeDefined(companyObjectMetadata); + companyObjectMetadataId = companyObjectMetadata.id; + + jestExpectToBeDefined(companyObjectMetadata.fieldsList); + const nameField = companyObjectMetadata.fieldsList.find( + (field: { name: string }) => field.name === 'name', + ); + + jestExpectToBeDefined(nameField); + companyNameFieldMetadataId = nameField.id; + + const { data: roleData } = await createOneRole({ + expectToFail: false, + input: { + label: 'Test Role For RLS Predicates', + description: 'A role for RLS predicate testing', + icon: 'IconSettings', + canUpdateAllSettings: false, + canAccessAllTools: true, + canReadAllObjectRecords: true, + canUpdateAllObjectRecords: false, + canSoftDeleteAllObjectRecords: false, + canDestroyAllObjectRecords: false, + canBeAssignedToUsers: true, + canBeAssignedToAgents: false, + canBeAssignedToApiKeys: false, + }, + }); + + createdRoleId = roleData?.createOneRole?.id; + jestExpectToBeDefined(createdRoleId); + }); + + afterAll(async () => { + await updateFeatureFlag({ + featureFlag: FeatureFlagKey.IS_ROW_LEVEL_PERMISSION_PREDICATES_ENABLED, + value: false, + expectToFail: false, + }); + + if (createdRoleId) { + await deleteOneRole({ + expectToFail: false, + input: { idToDelete: createdRoleId }, + }); + } + }); + + it('should upsert predicates and groups with minimal input', async () => { + const input: UpsertRowLevelPermissionPredicatesInput = { + roleId: createdRoleId, + objectMetadataId: companyObjectMetadataId, + predicates: [ + { + fieldMetadataId: companyNameFieldMetadataId, + operand: RowLevelPermissionPredicateOperand.CONTAINS, + }, + ], + predicateGroups: [], + }; + + const { data } = await upsertRowLevelPermissionPredicates({ + expectToFail: false, + input, + }); + + expect(data.upsertRowLevelPermissionPredicates).toBeDefined(); + expect(data.upsertRowLevelPermissionPredicates.predicates).toHaveLength(1); + expect( + data.upsertRowLevelPermissionPredicates.predicateGroups, + ).toHaveLength(0); + + expect(data.upsertRowLevelPermissionPredicates.predicates[0]).toMatchObject( + { + id: expect.any(String), + fieldMetadataId: companyNameFieldMetadataId, + objectMetadataId: companyObjectMetadataId, + operand: RowLevelPermissionPredicateOperand.CONTAINS, + roleId: createdRoleId, + rowLevelPermissionPredicateGroupId: null, + }, + ); + }); + + it('should upsert predicates with all fields', async () => { + const input: UpsertRowLevelPermissionPredicatesInput = { + roleId: createdRoleId, + objectMetadataId: companyObjectMetadataId, + predicates: [ + { + fieldMetadataId: companyNameFieldMetadataId, + operand: RowLevelPermissionPredicateOperand.IS, + value: 'Test Company', + subFieldName: null, + workspaceMemberFieldMetadataId: null, + workspaceMemberSubFieldName: null, + rowLevelPermissionPredicateGroupId: null, + positionInRowLevelPermissionPredicateGroup: 0, + }, + ], + predicateGroups: [], + }; + + const { data } = await upsertRowLevelPermissionPredicates({ + expectToFail: false, + input, + }); + + expect(data.upsertRowLevelPermissionPredicates.predicates).toHaveLength(1); + expect(data.upsertRowLevelPermissionPredicates.predicates[0]).toMatchObject( + { + id: expect.any(String), + fieldMetadataId: companyNameFieldMetadataId, + objectMetadataId: companyObjectMetadataId, + operand: RowLevelPermissionPredicateOperand.IS, + value: 'Test Company', + roleId: createdRoleId, + }, + ); + }); + + it('should upsert predicate groups with logical operators', async () => { + const input: UpsertRowLevelPermissionPredicatesInput = { + roleId: createdRoleId, + objectMetadataId: companyObjectMetadataId, + predicates: [], + predicateGroups: [ + { + objectMetadataId: companyObjectMetadataId, + logicalOperator: RowLevelPermissionPredicateGroupLogicalOperator.AND, + parentRowLevelPermissionPredicateGroupId: null, + positionInRowLevelPermissionPredicateGroup: 0, + }, + ], + }; + + const { data } = await upsertRowLevelPermissionPredicates({ + expectToFail: false, + input, + }); + + expect( + data.upsertRowLevelPermissionPredicates.predicateGroups, + ).toHaveLength(1); + expect( + data.upsertRowLevelPermissionPredicates.predicateGroups[0], + ).toMatchObject({ + id: expect.any(String), + objectMetadataId: companyObjectMetadataId, + logicalOperator: RowLevelPermissionPredicateGroupLogicalOperator.AND, + roleId: createdRoleId, + parentRowLevelPermissionPredicateGroupId: null, + }); + }); + + it('should upsert nested predicate groups', async () => { + const input: UpsertRowLevelPermissionPredicatesInput = { + roleId: createdRoleId, + objectMetadataId: companyObjectMetadataId, + predicates: [], + predicateGroups: [ + { + objectMetadataId: companyObjectMetadataId, + logicalOperator: RowLevelPermissionPredicateGroupLogicalOperator.AND, + parentRowLevelPermissionPredicateGroupId: null, + }, + ], + }; + + const { data: parentData } = await upsertRowLevelPermissionPredicates({ + expectToFail: false, + input, + }); + + const parentGroupId = + parentData.upsertRowLevelPermissionPredicates.predicateGroups[0]?.id; + + jestExpectToBeDefined(parentGroupId); + + const nestedInput: UpsertRowLevelPermissionPredicatesInput = { + roleId: createdRoleId, + objectMetadataId: companyObjectMetadataId, + predicates: [], + predicateGroups: [ + { + id: parentGroupId, + objectMetadataId: companyObjectMetadataId, + logicalOperator: RowLevelPermissionPredicateGroupLogicalOperator.AND, + parentRowLevelPermissionPredicateGroupId: null, + }, + { + objectMetadataId: companyObjectMetadataId, + logicalOperator: RowLevelPermissionPredicateGroupLogicalOperator.OR, + parentRowLevelPermissionPredicateGroupId: parentGroupId, + }, + ], + }; + + const { data: nestedData } = await upsertRowLevelPermissionPredicates({ + expectToFail: false, + input: nestedInput, + }); + + expect( + nestedData.upsertRowLevelPermissionPredicates.predicateGroups, + ).toHaveLength(2); + + const childGroup = + nestedData.upsertRowLevelPermissionPredicates.predicateGroups.find( + (group) => + group.parentRowLevelPermissionPredicateGroupId === parentGroupId, + ); + + expect(childGroup).toBeDefined(); + expect(childGroup?.logicalOperator).toBe( + RowLevelPermissionPredicateGroupLogicalOperator.OR, + ); + }); + + it('should update existing predicates and groups', async () => { + const createInput: UpsertRowLevelPermissionPredicatesInput = { + roleId: createdRoleId, + objectMetadataId: companyObjectMetadataId, + predicates: [ + { + fieldMetadataId: companyNameFieldMetadataId, + operand: RowLevelPermissionPredicateOperand.CONTAINS, + }, + ], + predicateGroups: [ + { + objectMetadataId: companyObjectMetadataId, + logicalOperator: RowLevelPermissionPredicateGroupLogicalOperator.AND, + }, + ], + }; + + const { data: createData } = await upsertRowLevelPermissionPredicates({ + expectToFail: false, + input: createInput, + }); + + const createdPredicateId = + createData.upsertRowLevelPermissionPredicates.predicates[0]?.id; + const createdGroupId = + createData.upsertRowLevelPermissionPredicates.predicateGroups[0]?.id; + + jestExpectToBeDefined(createdPredicateId); + jestExpectToBeDefined(createdGroupId); + + const updateInput: UpsertRowLevelPermissionPredicatesInput = { + roleId: createdRoleId, + objectMetadataId: companyObjectMetadataId, + predicates: [ + { + id: createdPredicateId, + fieldMetadataId: companyNameFieldMetadataId, + operand: RowLevelPermissionPredicateOperand.IS, + value: 'Updated Value', + }, + ], + predicateGroups: [ + { + id: createdGroupId, + objectMetadataId: companyObjectMetadataId, + logicalOperator: RowLevelPermissionPredicateGroupLogicalOperator.OR, + }, + ], + }; + + const { data: updateData } = await upsertRowLevelPermissionPredicates({ + expectToFail: false, + input: updateInput, + }); + + expect( + updateData.upsertRowLevelPermissionPredicates.predicates, + ).toHaveLength(1); + expect( + updateData.upsertRowLevelPermissionPredicates.predicates[0], + ).toMatchObject({ + id: createdPredicateId, + operand: RowLevelPermissionPredicateOperand.IS, + value: 'Updated Value', + }); + + expect( + updateData.upsertRowLevelPermissionPredicates.predicateGroups, + ).toHaveLength(1); + expect( + updateData.upsertRowLevelPermissionPredicates.predicateGroups[0], + ).toMatchObject({ + id: createdGroupId, + logicalOperator: RowLevelPermissionPredicateGroupLogicalOperator.OR, + }); + }); + + it('should delete predicates and groups by not including them', async () => { + const createInput: UpsertRowLevelPermissionPredicatesInput = { + roleId: createdRoleId, + objectMetadataId: companyObjectMetadataId, + predicates: [ + { + fieldMetadataId: companyNameFieldMetadataId, + operand: RowLevelPermissionPredicateOperand.CONTAINS, + }, + ], + predicateGroups: [ + { + objectMetadataId: companyObjectMetadataId, + logicalOperator: RowLevelPermissionPredicateGroupLogicalOperator.AND, + }, + ], + }; + + await upsertRowLevelPermissionPredicates({ + expectToFail: false, + input: createInput, + }); + + const deleteInput: UpsertRowLevelPermissionPredicatesInput = { + roleId: createdRoleId, + objectMetadataId: companyObjectMetadataId, + predicates: [], + predicateGroups: [], + }; + + const { data: deleteData } = await upsertRowLevelPermissionPredicates({ + expectToFail: false, + input: deleteInput, + }); + + expect( + deleteData.upsertRowLevelPermissionPredicates.predicates, + ).toHaveLength(0); + expect( + deleteData.upsertRowLevelPermissionPredicates.predicateGroups, + ).toHaveLength(0); + }); + + it('should upsert predicates with predicate groups', async () => { + const input: UpsertRowLevelPermissionPredicatesInput = { + roleId: createdRoleId, + objectMetadataId: companyObjectMetadataId, + predicates: [], + predicateGroups: [ + { + objectMetadataId: companyObjectMetadataId, + logicalOperator: RowLevelPermissionPredicateGroupLogicalOperator.AND, + }, + ], + }; + + const { data: groupData } = await upsertRowLevelPermissionPredicates({ + expectToFail: false, + input, + }); + + const groupId = + groupData.upsertRowLevelPermissionPredicates.predicateGroups[0]?.id; + + jestExpectToBeDefined(groupId); + + const predicateInput: UpsertRowLevelPermissionPredicatesInput = { + roleId: createdRoleId, + objectMetadataId: companyObjectMetadataId, + predicates: [ + { + fieldMetadataId: companyNameFieldMetadataId, + operand: RowLevelPermissionPredicateOperand.CONTAINS, + rowLevelPermissionPredicateGroupId: groupId, + positionInRowLevelPermissionPredicateGroup: 0, + }, + ], + predicateGroups: [ + { + id: groupId, + objectMetadataId: companyObjectMetadataId, + logicalOperator: RowLevelPermissionPredicateGroupLogicalOperator.AND, + }, + ], + }; + + const { data: predicateData } = await upsertRowLevelPermissionPredicates({ + expectToFail: false, + input: predicateInput, + }); + + expect( + predicateData.upsertRowLevelPermissionPredicates.predicates, + ).toHaveLength(1); + expect( + predicateData.upsertRowLevelPermissionPredicates.predicates[0], + ).toMatchObject({ + rowLevelPermissionPredicateGroupId: groupId, + positionInRowLevelPermissionPredicateGroup: 0, + }); + }); +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/row-level-permission-predicate/utils/upsert-row-level-permission-predicates-query-factory.util.ts b/packages/twenty-server/test/integration/metadata/suites/row-level-permission-predicate/utils/upsert-row-level-permission-predicates-query-factory.util.ts new file mode 100644 index 0000000000..1b74dc8162 --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/row-level-permission-predicate/utils/upsert-row-level-permission-predicates-query-factory.util.ts @@ -0,0 +1,42 @@ +import gql from 'graphql-tag'; +import { + ROW_LEVEL_PERMISSION_PREDICATE_GQL_FIELDS, + ROW_LEVEL_PERMISSION_PREDICATE_GROUP_GQL_FIELDS, +} from 'test/integration/constants/row-level-permission-predicate-gql-fields.constants'; +import { type PerformMetadataQueryParams } from 'test/integration/metadata/types/perform-metadata-query.type'; + +import { type UpsertRowLevelPermissionPredicatesInput } from 'src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/upsert-row-level-permission-predicates.input'; + +export const upsertRowLevelPermissionPredicatesQueryFactory = ({ + gqlFields = { + predicates: ROW_LEVEL_PERMISSION_PREDICATE_GQL_FIELDS, + predicateGroups: ROW_LEVEL_PERMISSION_PREDICATE_GROUP_GQL_FIELDS, + }, + input, +}: Omit< + PerformMetadataQueryParams, + 'gqlFields' +> & { + gqlFields?: { + predicates?: string; + predicateGroups?: string; + }; +}) => ({ + query: gql` + mutation UpsertRowLevelPermissionPredicates( + $input: UpsertRowLevelPermissionPredicatesInput! + ) { + upsertRowLevelPermissionPredicates(input: $input) { + predicates { + ${gqlFields.predicates} + } + predicateGroups { + ${gqlFields.predicateGroups} + } + } + } + `, + variables: { + input, + }, +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/row-level-permission-predicate/utils/upsert-row-level-permission-predicates.util.ts b/packages/twenty-server/test/integration/metadata/suites/row-level-permission-predicate/utils/upsert-row-level-permission-predicates.util.ts new file mode 100644 index 0000000000..c395c96905 --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/row-level-permission-predicate/utils/upsert-row-level-permission-predicates.util.ts @@ -0,0 +1,54 @@ +import { upsertRowLevelPermissionPredicatesQueryFactory } from 'test/integration/metadata/suites/row-level-permission-predicate/utils/upsert-row-level-permission-predicates-query-factory.util'; +import { makeMetadataAPIRequest } from 'test/integration/metadata/suites/utils/make-metadata-api-request.util'; +import { type CommonResponseBody } from 'test/integration/metadata/types/common-response-body.type'; +import { type PerformMetadataQueryParams } from 'test/integration/metadata/types/perform-metadata-query.type'; +import { warnIfErrorButNotExpectedToFail } from 'test/integration/metadata/utils/warn-if-error-but-not-expected-to-fail.util'; +import { warnIfNoErrorButExpectedToFail } from 'test/integration/metadata/utils/warn-if-no-error-but-expected-to-fail.util'; + +import { type UpsertRowLevelPermissionPredicatesInput } from 'src/engine/metadata-modules/row-level-permission-predicate/dtos/inputs/upsert-row-level-permission-predicates.input'; +import { type RowLevelPermissionPredicateGroupDTO } from 'src/engine/metadata-modules/row-level-permission-predicate/dtos/row-level-permission-predicate-group.dto'; +import { type RowLevelPermissionPredicateDTO } from 'src/engine/metadata-modules/row-level-permission-predicate/dtos/row-level-permission-predicate.dto'; + +export const upsertRowLevelPermissionPredicates = async ({ + input, + gqlFields, + expectToFail = false, +}: Omit< + PerformMetadataQueryParams, + 'gqlFields' +> & { + gqlFields?: { + predicates?: string; + predicateGroups?: string; + }; +}): CommonResponseBody<{ + upsertRowLevelPermissionPredicates: { + predicates: RowLevelPermissionPredicateDTO[]; + predicateGroups: RowLevelPermissionPredicateGroupDTO[]; + }; +}> => { + const graphqlOperation = upsertRowLevelPermissionPredicatesQueryFactory({ + input, + gqlFields, + }); + + const response = await makeMetadataAPIRequest(graphqlOperation); + + if (expectToFail === true) { + warnIfNoErrorButExpectedToFail({ + response, + errorMessage: + 'Row Level Permission Predicates upsert should have failed but did not', + }); + } + + if (expectToFail === false) { + warnIfErrorButNotExpectedToFail({ + response, + errorMessage: + 'Row Level Permission Predicates upsert has failed but should not', + }); + } + + return { data: response.body.data, errors: response.body.errors }; +};