From d13cc7c3497b7e07968eb9f8fd8096853926ea08 Mon Sep 17 00:00:00 2001 From: Weiko Date: Thu, 21 May 2026 13:56:06 +0200 Subject: [PATCH] Drop legacy rolePermissionFlag.flag column + fallback logic (#20730) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - **New fast migration** `2-7-instance-command-fast-1779600000000-finalize-role-permission-flag-cutover.ts`: - `DROP CONSTRAINT IDX_ROLE_PERMISSION_FLAG_FLAG_ROLE_ID_UNIQUE` - `ALTER COLUMN permissionFlagId SET NOT NULL` - `DROP COLUMN flag` - `down()` repopulates `flag` from the catalog via `permissionFlagId` and restores the old unique. - **Entity**: `RolePermissionFlagEntity` hides the `flag` column by using the new decorator + drops old `@Unique` decorator; `permissionFlagId` and the `permissionFlag` relation become non-nullable. - **Deletes** the synthesizer `synthesize-flat-permission-flag-from-flag.util.ts` and every fallback branch that used it (`from-role-permission-flag-entity-to-flat-role-permission-flag.util.ts`, `from-flat-role-permission-flag-to-role-permission-flag-dto.util.ts`, `permissions.service.ts`, `workspace-roles-permissions-cache.service.ts`, `fromRoleEntityToRoleDto.util.ts`, `flat-role-permission-flag-validator.service.ts`, `role-permission-flag.service.ts:getEffectiveUniversalIdentifier`). - **Write path**: ~~drops `flag` from `CreateRolePermissionFlagInput`, the create util, and the application-manifest converter.~~ - **Metadata configs**: ~~removes `flag` from `all-entity-properties-configuration-by-metadata-name.constant.ts` (rolePermissionFlag block)~~ and flips `permissionFlag.isNullable` to `false` in `all-many-to-one-metadata-relations.constant.ts`. ### Why the `flag` field stays declared in the entity The decorator (`@WasRemovedInUpgrade`) is the right tool for the lifecycle marker, but it's a **reflect-metadata** runtime decorator — TypeScript can't see it at compile time. So while the adapter ([`UpgradeAwareEntityMetadataAdapter`](packages/twenty-server/src/engine/twenty-orm/upgrade-aware/upgrade-aware-entity-metadata.adapter.ts)) now correctly flips `isSelect`/`isInsert`/`isUpdate` to `false` once the drop migration's cursor is crossed, the *static* TypeScript types derived from `RolePermissionFlagEntity` (`UniversalFlatRolePermissionFlag`, `FlatRolePermissionFlag`, `MetadataEntityPropertyConfiguration<'rolePermissionFlag'>`, etc.) still see `flag` as a required scalar property — because the entity declares it. That means every producer of one of those derived types must include `flag`: - `from-create-role-permission-flag-input-to-flat-role-permission-flag-to-create.util.ts` plumbs it through. - `from-permission-flag-to-universal-flat-role-permission-flag.util.ts` (the application-manifest converter) sets `flag: permissionFlag.flag`. - `all-entity-properties-configuration-by-metadata-name.constant.ts` has a `flag` entry under `rolePermissionFlag`. - `CreateRolePermissionFlagInput` keeps the `flag` field. - `RolePermissionFlagService.upsertPermissionFlags` passes `flag: permissionFlag.key as PermissionFlagType` to the create util. Explored phantom-brand approach (`RemovedInUpgrade` wrapper on the field type, key-filter mapped type applied inside `ScalarFlatEntity` / `UniversalFlatEntityFrom`) but previous commands could have `flag === undefined` (downcast from the brand since we can't compare with UpgradeMigrationName like we do with a decorator). That's a **silent-read** failure mode: compiles fine, comparisons against `flag` silently always-false, no error surfaces. Probably worth too much risk for what's a small amount of plumbing? The eventual full deletion of `flag` (entity field included) is a future cleanup once we drop cross-upgrade support for versions ≤ 2.6 Note: Not sure if this PR (and even the decorator) is really needed in the end, seems we need to keep a lot of code in place to handle legacy. Maybe a simple noop [At]Deprecated is enough @charlesBochet (and a migration to set the column nullable if that was not the case before + remove associated constraints) --- ...0-finalize-role-permission-flag-cutover.ts | 69 +++++++++++++++++++ .../instance-commands.constant.ts | 2 + ...many-to-one-metadata-relations.constant.ts | 2 +- ...ize-flat-permission-flag-from-flag.util.ts | 57 --------------- ...ntity-to-flat-role-permission-flag.util.ts | 12 ++-- .../permissions/permissions.service.ts | 3 +- .../role-permission-flag.entity.ts | 12 ++-- .../role-permission-flag.service.ts | 17 ++--- ...g-to-role-permission-flag-dto.util.spec.ts | 23 ++++--- ...n-flag-to-role-permission-flag-dto.util.ts | 31 ++++----- ...rkspace-roles-permissions-cache.service.ts | 3 +- .../utils/fromRoleEntityToRoleDto.util.ts | 3 +- ...rade-aware-entity-metadata.adapter.spec.ts | 2 + .../upgrade-aware-entity-metadata.adapter.ts | 8 +++ ...-role-permission-flag-validator.service.ts | 26 +++---- 15 files changed, 139 insertions(+), 131 deletions(-) create mode 100644 packages/twenty-server/src/database/commands/upgrade-version-command/2-7/2-7-instance-command-fast-1779600000000-finalize-role-permission-flag-cutover.ts delete mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-permission-flag/utils/synthesize-flat-permission-flag-from-flag.util.ts diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/2-7/2-7-instance-command-fast-1779600000000-finalize-role-permission-flag-cutover.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/2-7/2-7-instance-command-fast-1779600000000-finalize-role-permission-flag-cutover.ts new file mode 100644 index 0000000000..5bf5745def --- /dev/null +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/2-7/2-7-instance-command-fast-1779600000000-finalize-role-permission-flag-cutover.ts @@ -0,0 +1,69 @@ +import { QueryRunner } from 'typeorm'; + +import { RegisteredInstanceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-instance-command.decorator'; +import { FastInstanceCommand } from 'src/engine/core-modules/upgrade/interfaces/fast-instance-command.interface'; + +@RegisteredInstanceCommand('2.7.0', 1779600000000) +export class FinalizeRolePermissionFlagCutoverFastInstanceCommand + implements FastInstanceCommand +{ + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "core"."rolePermissionFlag" + DROP CONSTRAINT IF EXISTS "IDX_ROLE_PERMISSION_FLAG_FLAG_ROLE_ID_UNIQUE"`, + ); + + await queryRunner.query( + `ALTER TABLE "core"."rolePermissionFlag" + ALTER COLUMN "permissionFlagId" SET NOT NULL`, + ); + + await queryRunner.query( + `ALTER TABLE "core"."rolePermissionFlag" DROP COLUMN "flag"`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "core"."rolePermissionFlag" + ADD COLUMN IF NOT EXISTS "flag" varchar`, + ); + + await queryRunner.query( + `UPDATE "core"."rolePermissionFlag" rolePermissionFlag + SET "flag" = permissionFlag."key" + FROM "core"."permissionFlag" permissionFlag + WHERE permissionFlag."id" = rolePermissionFlag."permissionFlagId" + AND rolePermissionFlag."flag" IS NULL`, + ); + + await queryRunner.query( + `DO $$ + BEGIN + IF EXISTS ( + SELECT 1 + FROM "core"."rolePermissionFlag" + WHERE "flag" IS NULL + ) THEN + RAISE EXCEPTION 'Unable to restore rolePermissionFlag.flag'; + END IF; + END $$`, + ); + + await queryRunner.query( + `ALTER TABLE "core"."rolePermissionFlag" + ALTER COLUMN "flag" SET NOT NULL`, + ); + + await queryRunner.query( + `ALTER TABLE "core"."rolePermissionFlag" + ADD CONSTRAINT "IDX_ROLE_PERMISSION_FLAG_FLAG_ROLE_ID_UNIQUE" + UNIQUE ("flag", "roleId")`, + ); + + await queryRunner.query( + `ALTER TABLE "core"."rolePermissionFlag" + ALTER COLUMN "permissionFlagId" DROP NOT NULL`, + ); + } +} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/instance-commands.constant.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/instance-commands.constant.ts index 7e2baf24ea..e7e66cd711 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/instance-commands.constant.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/instance-commands.constant.ts @@ -52,6 +52,7 @@ import { AddSubFieldNameToViewSortFastInstanceCommand } from 'src/database/comma import { DropPostgresCredentialsTableFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-5/2-5-instance-command-fast-1798500000000-drop-postgres-credentials-table'; import { AddRelationTargetFieldMetadataIdToViewFilterFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-6/2-6-instance-command-fast-1798000005000-add-relation-target-field-metadata-id-to-view-filter'; import { AddChannelSyncStageIndexesFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-6/2-6-instance-command-fast-1798000010000-add-channel-sync-stage-indexes'; +import { FinalizeRolePermissionFlagCutoverFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-7/2-7-instance-command-fast-1779600000000-finalize-role-permission-flag-cutover'; export const INSTANCE_COMMANDS = [ AddViewFieldGroupIdIndexOnViewFieldFastInstanceCommand, @@ -106,4 +107,5 @@ export const INSTANCE_COMMANDS = [ DropPostgresCredentialsTableFastInstanceCommand, AddRelationTargetFieldMetadataIdToViewFilterFastInstanceCommand, AddChannelSyncStageIndexesFastInstanceCommand, + FinalizeRolePermissionFlagCutoverFastInstanceCommand, ]; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-many-to-one-metadata-relations.constant.ts b/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-many-to-one-metadata-relations.constant.ts index 96d02a00c4..1548c668a4 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-many-to-one-metadata-relations.constant.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-many-to-one-metadata-relations.constant.ts @@ -314,7 +314,7 @@ export const ALL_MANY_TO_ONE_METADATA_RELATIONS = { metadataName: 'permissionFlag', foreignKey: 'permissionFlagId', inverseOneToManyProperty: 'rolePermissionFlags', - isNullable: true, + isNullable: false, universalForeignKey: 'permissionFlagUniversalIdentifier', }, }, diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-permission-flag/utils/synthesize-flat-permission-flag-from-flag.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-permission-flag/utils/synthesize-flat-permission-flag-from-flag.util.ts deleted file mode 100644 index df2266e2c0..0000000000 --- a/packages/twenty-server/src/engine/metadata-modules/flat-permission-flag/utils/synthesize-flat-permission-flag-from-flag.util.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { - PermissionFlagType, - SystemPermissionFlag, -} from 'twenty-shared/constants'; - -import { type FlatPermissionFlag } from 'src/engine/metadata-modules/flat-permission-flag/types/flat-permission-flag.type'; -import { - STANDARD_PERMISSION_FLAG_DEFINITIONS, - type StandardPermissionFlagDefinition, -} from 'src/engine/metadata-modules/permission-flag/constants/standard-permission-flag-definitions.constant'; - -const STANDARD_PERMISSION_FLAG_DEFINITION_BY_KEY: Record< - PermissionFlagType, - StandardPermissionFlagDefinition -> = STANDARD_PERMISSION_FLAG_DEFINITIONS.reduce( - (accumulator, definition) => { - accumulator[definition.key] = definition; - - return accumulator; - }, - {} as Record, -); - -const SYNTHESIZED_TIMESTAMP = new Date(0).toISOString(); - -// TODO: Remove this once we have a proper permission flag catalog backfilled -export const synthesizeFlatPermissionFlagFromFlag = ({ - flag, - workspaceId, - applicationId, - applicationUniversalIdentifier, -}: { - flag: PermissionFlagType; - workspaceId: string; - applicationId: string; - applicationUniversalIdentifier: string; -}): FlatPermissionFlag => { - const definition = STANDARD_PERMISSION_FLAG_DEFINITION_BY_KEY[flag]; - const universalIdentifier = SystemPermissionFlag[flag]; - - return { - id: universalIdentifier, - universalIdentifier, - workspaceId, - applicationId, - applicationUniversalIdentifier, - key: flag, - label: definition.label, - description: definition.description, - icon: definition.icon, - permissionType: definition.permissionType, - rolePermissionFlagIds: [], - rolePermissionFlagUniversalIdentifiers: [], - createdAt: SYNTHESIZED_TIMESTAMP, - updatedAt: SYNTHESIZED_TIMESTAMP, - }; -}; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-role-permission-flag/utils/from-role-permission-flag-entity-to-flat-role-permission-flag.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-role-permission-flag/utils/from-role-permission-flag-entity-to-flat-role-permission-flag.util.ts index 4201636c2e..9c9b22b442 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-role-permission-flag/utils/from-role-permission-flag-entity-to-flat-role-permission-flag.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-role-permission-flag/utils/from-role-permission-flag-entity-to-flat-role-permission-flag.util.ts @@ -1,4 +1,3 @@ -import { SystemPermissionFlag } from 'twenty-shared/constants'; import { isDefined, removePropertiesFromRecord } from 'twenty-shared/utils'; import { @@ -43,13 +42,10 @@ export const fromRolePermissionFlagEntityToFlatRolePermissionFlag = ({ ); } - const permissionFlagUniversalIdentifier = isDefined( - rolePermissionFlagEntity.permissionFlagId, - ) - ? permissionFlagIdToUniversalIdentifierMap.get( - rolePermissionFlagEntity.permissionFlagId, - ) - : SystemPermissionFlag[rolePermissionFlagEntity.flag]; + const permissionFlagUniversalIdentifier = + permissionFlagIdToUniversalIdentifierMap.get( + rolePermissionFlagEntity.permissionFlagId, + ); if (!isDefined(permissionFlagUniversalIdentifier)) { throw new FlatEntityMapsException( diff --git a/packages/twenty-server/src/engine/metadata-modules/permissions/permissions.service.ts b/packages/twenty-server/src/engine/metadata-modules/permissions/permissions.service.ts index 3ef8fd66e8..05b1154eec 100644 --- a/packages/twenty-server/src/engine/metadata-modules/permissions/permissions.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/permissions/permissions.service.ts @@ -260,8 +260,7 @@ export class PermissionsService { return rolePermissionFlags.some( (rolePermissionFlag) => - (rolePermissionFlag.permissionFlag?.universalIdentifier ?? - SystemPermissionFlag[rolePermissionFlag.flag]) === + rolePermissionFlag.permissionFlag.universalIdentifier === permissionFlagUniversalIdentifier, ); } diff --git a/packages/twenty-server/src/engine/metadata-modules/role-permission-flag/role-permission-flag.entity.ts b/packages/twenty-server/src/engine/metadata-modules/role-permission-flag/role-permission-flag.entity.ts index 3a328c6f8e..bfc353e9b5 100644 --- a/packages/twenty-server/src/engine/metadata-modules/role-permission-flag/role-permission-flag.entity.ts +++ b/packages/twenty-server/src/engine/metadata-modules/role-permission-flag/role-permission-flag.entity.ts @@ -13,6 +13,7 @@ import { } from 'typeorm'; import { WasIntroducedInUpgrade } from 'src/engine/core-modules/upgrade/decorators/was-introduced-in-upgrade.decorator'; +import { WasRemovedInUpgrade } from 'src/engine/core-modules/upgrade/decorators/was-removed-in-upgrade.decorator'; import { WasRenamedInUpgrade } from 'src/engine/core-modules/upgrade/decorators/was-renamed-in-upgrade.decorator'; import { PermissionFlagEntity } from 'src/engine/metadata-modules/permission-flag/permission-flag.entity'; import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity'; @@ -26,7 +27,6 @@ import { SyncableEntity } from 'src/engine/workspace-manager/types/syncable-enti '2.6.0_RenamePermissionFlagToRolePermissionFlagFastInstanceCommand_1778235340020', }, ]) -@Unique('IDX_ROLE_PERMISSION_FLAG_FLAG_ROLE_ID_UNIQUE', ['flag', 'roleId']) @Unique('IDX_ROLE_PERMISSION_FLAG_PERMISSION_FLAG_ID_ROLE_ID_UNIQUE', [ 'permissionFlagId', 'roleId', @@ -46,6 +46,10 @@ export class RolePermissionFlagEntity extends SyncableEntity { @JoinColumn({ name: 'roleId' }) role: Relation; + @WasRemovedInUpgrade({ + upgradeCommandName: + '2.7.0_FinalizeRolePermissionFlagCutoverFastInstanceCommand_1779600000000', + }) @Column({ nullable: false, type: 'varchar' }) flag: PermissionFlagType; @@ -53,8 +57,8 @@ export class RolePermissionFlagEntity extends SyncableEntity { upgradeCommandName: '2.6.0_LinkRolePermissionFlagToPermissionFlagFastInstanceCommand_1778235340022', }) - @Column({ nullable: true, type: 'uuid' }) - permissionFlagId: string | null; + @Column({ nullable: false, type: 'uuid' }) + permissionFlagId: string; @ManyToOne( () => PermissionFlagEntity, @@ -64,7 +68,7 @@ export class RolePermissionFlagEntity extends SyncableEntity { }, ) @JoinColumn({ name: 'permissionFlagId' }) - permissionFlag: Relation | null; + permissionFlag: Relation; @CreateDateColumn({ type: 'timestamptz' }) createdAt: Date; diff --git a/packages/twenty-server/src/engine/metadata-modules/role-permission-flag/role-permission-flag.service.ts b/packages/twenty-server/src/engine/metadata-modules/role-permission-flag/role-permission-flag.service.ts index da8cfad398..c511b389d3 100644 --- a/packages/twenty-server/src/engine/metadata-modules/role-permission-flag/role-permission-flag.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/role-permission-flag/role-permission-flag.service.ts @@ -1,10 +1,7 @@ import { Injectable } from '@nestjs/common'; import { msg } from '@lingui/core/macro'; -import { - type PermissionFlagType, - SystemPermissionFlag, -} from 'twenty-shared/constants'; +import { type PermissionFlagType } from 'twenty-shared/constants'; import { isDefined } from 'twenty-shared/utils'; import { ApplicationService } from 'src/engine/core-modules/application/application.service'; @@ -98,12 +95,10 @@ export class RolePermissionFlagService { .map(({ permissionFlag }) => permissionFlag?.universalIdentifier) .filter(isDefined), ); - const getEffectiveUniversalIdentifier = ( - pf: FlatRolePermissionFlag, - ): string => - pf.permissionFlagUniversalIdentifier ?? SystemPermissionFlag[pf.flag]; const existingSet = new Set( - currentRolePermissionFlagsForRole.map(getEffectiveUniversalIdentifier), + currentRolePermissionFlagsForRole.map( + (pf) => pf.permissionFlagUniversalIdentifier, + ), ); const { workspaceCustomFlatApplication } = @@ -132,12 +127,12 @@ export class RolePermissionFlagService { ); const flatEntityToDelete = currentRolePermissionFlagsForRole.filter( - (pf) => !inputSet.has(getEffectiveUniversalIdentifier(pf)), + (pf) => !inputSet.has(pf.permissionFlagUniversalIdentifier), ); if (flatEntityToCreate.length === 0 && flatEntityToDelete.length === 0) { return currentRolePermissionFlagsForRole.filter((pf) => - inputSet.has(getEffectiveUniversalIdentifier(pf)), + inputSet.has(pf.permissionFlagUniversalIdentifier), ); } diff --git a/packages/twenty-server/src/engine/metadata-modules/role-permission-flag/utils/__tests__/from-flat-role-permission-flag-to-role-permission-flag-dto.util.spec.ts b/packages/twenty-server/src/engine/metadata-modules/role-permission-flag/utils/__tests__/from-flat-role-permission-flag-to-role-permission-flag-dto.util.spec.ts index 99891689e4..3d53908628 100644 --- a/packages/twenty-server/src/engine/metadata-modules/role-permission-flag/utils/__tests__/from-flat-role-permission-flag-to-role-permission-flag-dto.util.spec.ts +++ b/packages/twenty-server/src/engine/metadata-modules/role-permission-flag/utils/__tests__/from-flat-role-permission-flag-to-role-permission-flag-dto.util.spec.ts @@ -3,6 +3,10 @@ import { PermissionFlagType } from 'twenty-shared/constants'; import { type FlatPermissionFlagMaps } from 'src/engine/metadata-modules/flat-permission-flag/types/flat-permission-flag-maps.type'; import { type FlatPermissionFlag } from 'src/engine/metadata-modules/flat-permission-flag/types/flat-permission-flag.type'; import { type FlatRolePermissionFlag } from 'src/engine/metadata-modules/flat-role-permission-flag/types/flat-role-permission-flag.type'; +import { + PermissionsException, + PermissionsExceptionCode, +} from 'src/engine/metadata-modules/permissions/permissions.exception'; import { fromFlatRolePermissionFlagToRolePermissionFlagDto } from 'src/engine/metadata-modules/role-permission-flag/utils/from-flat-role-permission-flag-to-role-permission-flag-dto.util'; const buildFlatRolePermissionFlag = ( @@ -11,11 +15,7 @@ const buildFlatRolePermissionFlag = ( ({ id: 'role-permission-flag-id', roleId: 'role-id', - workspaceId: 'workspace-id', - applicationId: 'application-id', - applicationUniversalIdentifier: 'application-universal-id', permissionFlagUniversalIdentifier: 'permission-flag-universal-id', - flag: PermissionFlagType.WORKSPACE, ...overrides, }) as FlatRolePermissionFlag; @@ -60,8 +60,8 @@ describe('fromFlatRolePermissionFlagToRolePermissionFlagDto', () => { }); }); - it('falls back to the legacy flag column when the catalog is missing the entry', () => { - expect( + it('throws a permissions exception when the permission flag is missing', () => { + expect(() => fromFlatRolePermissionFlagToRolePermissionFlagDto( buildFlatRolePermissionFlag(), { @@ -70,10 +70,11 @@ describe('fromFlatRolePermissionFlagToRolePermissionFlagDto', () => { universalIdentifiersByApplicationId: {}, }, ), - ).toEqual({ - id: 'role-permission-flag-id', - roleId: 'role-id', - flag: PermissionFlagType.WORKSPACE, - }); + ).toThrow( + new PermissionsException( + 'Permission flag permission-flag-universal-id not found', + PermissionsExceptionCode.PERMISSION_NOT_FOUND, + ), + ); }); }); diff --git a/packages/twenty-server/src/engine/metadata-modules/role-permission-flag/utils/from-flat-role-permission-flag-to-role-permission-flag-dto.util.ts b/packages/twenty-server/src/engine/metadata-modules/role-permission-flag/utils/from-flat-role-permission-flag-to-role-permission-flag-dto.util.ts index 874ed0f554..63dee33461 100644 --- a/packages/twenty-server/src/engine/metadata-modules/role-permission-flag/utils/from-flat-role-permission-flag-to-role-permission-flag-dto.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/role-permission-flag/utils/from-flat-role-permission-flag-to-role-permission-flag-dto.util.ts @@ -2,31 +2,28 @@ import { PermissionFlagType } from 'twenty-shared/constants'; import { isDefined } from 'twenty-shared/utils'; import { type FlatPermissionFlagMaps } from 'src/engine/metadata-modules/flat-permission-flag/types/flat-permission-flag-maps.type'; -import { synthesizeFlatPermissionFlagFromFlag } from 'src/engine/metadata-modules/flat-permission-flag/utils/synthesize-flat-permission-flag-from-flag.util'; import { type FlatRolePermissionFlag } from 'src/engine/metadata-modules/flat-role-permission-flag/types/flat-role-permission-flag.type'; +import { + PermissionsException, + PermissionsExceptionCode, +} from 'src/engine/metadata-modules/permissions/permissions.exception'; import { type RolePermissionFlagDTO } from 'src/engine/metadata-modules/role-permission-flag/dtos/role-permission-flag.dto'; export const fromFlatRolePermissionFlagToRolePermissionFlagDto = ( flatRolePermissionFlag: FlatRolePermissionFlag, flatPermissionFlagMaps: FlatPermissionFlagMaps, ): RolePermissionFlagDTO => { - const catalogPermissionFlag = isDefined( - flatRolePermissionFlag.permissionFlagUniversalIdentifier, - ) - ? flatPermissionFlagMaps.byUniversalIdentifier[ - flatRolePermissionFlag.permissionFlagUniversalIdentifier - ] - : undefined; + const permissionFlag = + flatPermissionFlagMaps.byUniversalIdentifier[ + flatRolePermissionFlag.permissionFlagUniversalIdentifier + ]; - const permissionFlag = isDefined(catalogPermissionFlag) - ? catalogPermissionFlag - : synthesizeFlatPermissionFlagFromFlag({ - flag: flatRolePermissionFlag.flag, - workspaceId: flatRolePermissionFlag.workspaceId, - applicationId: flatRolePermissionFlag.applicationId, - applicationUniversalIdentifier: - flatRolePermissionFlag.applicationUniversalIdentifier, - }); + if (!isDefined(permissionFlag)) { + throw new PermissionsException( + `Permission flag ${flatRolePermissionFlag.permissionFlagUniversalIdentifier} not found`, + PermissionsExceptionCode.PERMISSION_NOT_FOUND, + ); + } return { id: flatRolePermissionFlag.id, diff --git a/packages/twenty-server/src/engine/metadata-modules/role/services/workspace-roles-permissions-cache.service.ts b/packages/twenty-server/src/engine/metadata-modules/role/services/workspace-roles-permissions-cache.service.ts index 3dec732c91..08b9c94457 100644 --- a/packages/twenty-server/src/engine/metadata-modules/role/services/workspace-roles-permissions-cache.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/role/services/workspace-roles-permissions-cache.service.ts @@ -283,8 +283,7 @@ export class WorkspaceRolesPermissionsCacheService extends WorkspaceCacheProvide const hasPermissionFromSettingPermissions = isDefined( rolePermissionFlags.find( (rolePermissionFlag) => - (rolePermissionFlag.permissionFlag?.universalIdentifier ?? - SystemPermissionFlag[rolePermissionFlag.flag]) === + rolePermissionFlag.permissionFlag.universalIdentifier === permissionFlagUniversalIdentifier, ), ); diff --git a/packages/twenty-server/src/engine/metadata-modules/role/utils/fromRoleEntityToRoleDto.util.ts b/packages/twenty-server/src/engine/metadata-modules/role/utils/fromRoleEntityToRoleDto.util.ts index ade62e9c74..93f3e3cec3 100644 --- a/packages/twenty-server/src/engine/metadata-modules/role/utils/fromRoleEntityToRoleDto.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/role/utils/fromRoleEntityToRoleDto.util.ts @@ -24,8 +24,7 @@ export const fromRoleEntityToRoleDto = (role: RoleEntity): RoleDTO => { permissionFlags: role.rolePermissionFlags?.map((rolePermissionFlag) => ({ id: rolePermissionFlag.id, roleId: rolePermissionFlag.roleId, - flag: (rolePermissionFlag.permissionFlag?.key ?? - rolePermissionFlag.flag) as PermissionFlagType, + flag: rolePermissionFlag.permissionFlag.key as PermissionFlagType, })), objectPermissions: role.objectPermissions, fieldPermissions: role.fieldPermissions, diff --git a/packages/twenty-server/src/engine/twenty-orm/upgrade-aware/__tests__/upgrade-aware-entity-metadata.adapter.spec.ts b/packages/twenty-server/src/engine/twenty-orm/upgrade-aware/__tests__/upgrade-aware-entity-metadata.adapter.spec.ts index c99fc4e321..ca445785de 100644 --- a/packages/twenty-server/src/engine/twenty-orm/upgrade-aware/__tests__/upgrade-aware-entity-metadata.adapter.spec.ts +++ b/packages/twenty-server/src/engine/twenty-orm/upgrade-aware/__tests__/upgrade-aware-entity-metadata.adapter.spec.ts @@ -151,5 +151,7 @@ describe('UpgradeAwareEntityMetadataAdapter', () => { expect(visibleColumn.isSelect).toBe(true); expect(visibleColumn.isInsert).toBe(true); expect(visibleColumn.isUpdate).toBe(true); + + expect(metadata.columns).toEqual([visibleColumn]); }); }); diff --git a/packages/twenty-server/src/engine/twenty-orm/upgrade-aware/upgrade-aware-entity-metadata.adapter.ts b/packages/twenty-server/src/engine/twenty-orm/upgrade-aware/upgrade-aware-entity-metadata.adapter.ts index fc2b6ef13f..88cee8685f 100644 --- a/packages/twenty-server/src/engine/twenty-orm/upgrade-aware/upgrade-aware-entity-metadata.adapter.ts +++ b/packages/twenty-server/src/engine/twenty-orm/upgrade-aware/upgrade-aware-entity-metadata.adapter.ts @@ -23,6 +23,7 @@ type EntityMetadataSnapshot = { tableName: string; tablePath: string; givenTableName: string | undefined; + canonicalColumns: ReadonlyArray; columnDatabaseNamesByPropertyName: ReadonlyMap; columnSelectByPropertyName: ReadonlyMap; columnInsertByPropertyName: ReadonlyMap; @@ -135,6 +136,7 @@ export class UpgradeAwareEntityMetadataAdapter implements OnModuleInit { tableName: metadata.tableName, tablePath: metadata.tablePath, givenTableName: metadata.givenTableName, + canonicalColumns: [...metadata.columns], columnDatabaseNamesByPropertyName, columnSelectByPropertyName, columnInsertByPropertyName, @@ -277,9 +279,15 @@ export class UpgradeAwareEntityMetadataAdapter implements OnModuleInit { metadata.givenTableName = resolved.effectiveTableName; } + metadata.columns = [...snapshot.canonicalColumns]; + for (const column of metadata.columns) { this.applyColumnShape({ column, snapshot, resolved }); } + + metadata.columns = metadata.columns.filter( + (column) => !resolved.hiddenPropertyNames.has(column.propertyName), + ); } private applyColumnShape({ diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-role-permission-flag-validator.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-role-permission-flag-validator.service.ts index 087c31f418..78df941dd2 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-role-permission-flag-validator.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-role-permission-flag-validator.service.ts @@ -80,24 +80,18 @@ export class FlatRolePermissionFlagValidatorService { } } - if ( - isDefined( + const referencedPermissionFlag = findFlatEntityByUniversalIdentifier({ + universalIdentifier: flatRolePermissionFlagToValidate.permissionFlagUniversalIdentifier, - ) - ) { - const referencedPermissionFlag = findFlatEntityByUniversalIdentifier({ - universalIdentifier: - flatRolePermissionFlagToValidate.permissionFlagUniversalIdentifier, - flatEntityMaps: flatPermissionFlagMaps, - }); + flatEntityMaps: flatPermissionFlagMaps, + }); - if (!isDefined(referencedPermissionFlag)) { - validationResult.errors.push({ - code: PermissionsExceptionCode.INVALID_SETTING, - message: t`Permission flag not found`, - userFriendlyMessage: msg`Invalid permission setting`, - }); - } + if (!isDefined(referencedPermissionFlag)) { + validationResult.errors.push({ + code: PermissionsExceptionCode.INVALID_SETTING, + message: t`Permission flag not found`, + userFriendlyMessage: msg`Invalid permission setting`, + }); } const duplicateForSameRole = Object.values(