From 0a11add616b1237090a08d7660e78b8efb42b1dc Mon Sep 17 00:00:00 2001 From: Weiko Date: Wed, 10 Dec 2025 15:45:04 +0100 Subject: [PATCH] Fix sync role v1 (#16459) ## Context SyncMetadata for role update was broken when those roles had permission flags in them. This PR https://github.com/twentyhq/twenty/issues/16365 introduced a change in all existing standard roles which probably triggered the update of faulty roles for the first time. Down the line, some properties from flat were not omit before querying the DB (in this case permissionFlagIds does not exist as a column in role table) The fix is definitely not great, this also probably broke the update of permissionFlags for existing STANDARD roles but it's not subject to change (It actually never changed. Again, what has been updated was not the permission flag but a new boolean introduced 2 days ago which forced the "UPDATE" path in the sync which was probably never triggered before, or at least not for roles with permission flags which are "quite" new). Also we are about to remove this whole code in favor of the new migration v2 --- .../flat-role-editable-properties.constant.ts | 1 + ...ndard-role-definition-to-flat-role.util.ts | 3 ++- .../comparators/workspace-role.comparator.ts | 22 ++++++------------- .../services/workspace-sync-role.service.ts | 10 ++++++++- 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-role/constants/flat-role-editable-properties.constant.ts b/packages/twenty-server/src/engine/metadata-modules/flat-role/constants/flat-role-editable-properties.constant.ts index 4338974bce..2358f6516f 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-role/constants/flat-role-editable-properties.constant.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-role/constants/flat-role-editable-properties.constant.ts @@ -13,4 +13,5 @@ export const FLAT_ROLE_EDITABLE_PROPERTIES: (keyof FlatRole)[] = [ 'canBeAssignedToUsers', 'canBeAssignedToAgents', 'canBeAssignedToApiKeys', + 'canBeAssignedToApplications', ]; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-role/utils/from-standard-role-definition-to-flat-role.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-role/utils/from-standard-role-definition-to-flat-role.util.ts index e362a7c355..7c88e82168 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-role/utils/from-standard-role-definition-to-flat-role.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-role/utils/from-standard-role-definition-to-flat-role.util.ts @@ -1,3 +1,4 @@ +import { removePropertiesFromRecord } from 'twenty-shared/utils'; import { v4 } from 'uuid'; import { type FlatRole } from 'src/engine/metadata-modules/flat-role/types/flat-role.type'; @@ -10,7 +11,7 @@ export const fromStandardRoleDefinitionToFlatRole = ( const createdAt = new Date().toISOString(); return { - ...standardRoleDefinition, + ...removePropertiesFromRecord(standardRoleDefinition, ['permissionFlags']), id: v4(), workspaceId, universalIdentifier: standardRoleDefinition.standardId || v4(), diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/comparators/workspace-role.comparator.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/comparators/workspace-role.comparator.ts index caa29fe687..8aa027a1bf 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/comparators/workspace-role.comparator.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/comparators/workspace-role.comparator.ts @@ -5,6 +5,7 @@ import { type FromTo } from 'twenty-shared/types'; import { ComparatorAction } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/comparator.interface'; +import { ALL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY } from 'src/engine/metadata-modules/flat-entity/constant/all-flat-entity-properties-to-compare-and-stringify.constant'; import { type FlatRole } from 'src/engine/metadata-modules/flat-role/types/flat-role.type'; import { type RoleEntity } from 'src/engine/metadata-modules/role/role.entity'; import { transformMetadataForComparison } from 'src/engine/workspace-manager/workspace-sync-metadata/comparators/utils/transform-metadata-for-comparison.util'; @@ -28,19 +29,6 @@ type RoleComparatorResult = type WorkspaceRoleComparatorArgs = FromTo; -const rolePropertiesToIgnore = [ - 'id', - 'createdAt', - 'updatedAt', - 'workspaceId', - 'roleTargets', - 'permissionFlags', - 'objectPermissions', - 'fieldPermissions', - 'universalIdentifier', - 'applicationId', -]; - @Injectable() export class WorkspaceRoleComparator { compare({ @@ -54,13 +42,17 @@ export class WorkspaceRoleComparator { const fromRoleMap = transformMetadataForComparison(fromFlatRoles, { shouldIgnoreProperty: (property) => - rolePropertiesToIgnore.includes(property), + !ALL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY.role.propertiesToCompare.includes( + property as keyof FlatRole, + ), keyFactory, }); const toRoleMap = transformMetadataForComparison(toFlatRoles, { shouldIgnoreProperty: (property) => - rolePropertiesToIgnore.includes(property), + !ALL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY.role.propertiesToCompare.includes( + property as keyof FlatRole, + ), keyFactory, }); diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/services/workspace-sync-role.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/services/workspace-sync-role.service.ts index 7563761ce2..bbc82367a1 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/services/workspace-sync-role.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/services/workspace-sync-role.service.ts @@ -1,8 +1,8 @@ import { Injectable, Logger } from '@nestjs/common'; +import { PermissionFlagType } from 'twenty-shared/constants'; import { removePropertiesFromRecord } from 'twenty-shared/utils'; import { IsNull, Not, type EntityManager, type Repository } from 'typeorm'; -import { PermissionFlagType } from 'twenty-shared/constants'; import { ComparatorAction } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/comparator.interface'; import { type WorkspaceSyncContext } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/workspace-sync-context.interface'; @@ -65,6 +65,10 @@ export class WorkspaceSyncRoleService { const flatRoleData = removePropertiesFromRecord(roleToCreate, [ 'universalIdentifier', 'id', + 'permissionFlagIds', + 'fieldPermissionIds', + 'objectPermissionIds', + 'roleTargetIds', ]); const createdRole = await roleRepository.save({ @@ -94,6 +98,10 @@ export class WorkspaceSyncRoleService { 'id', 'universalIdentifier', 'workspaceId', + 'permissionFlagIds', + 'fieldPermissionIds', + 'objectPermissionIds', + 'roleTargetIds', ]); await roleRepository.update({ id: roleToUpdate.id }, flatRoleData);