diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-application-variable/utils/from-application-variable-entity-to-flat-application-variable.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-application-variable/utils/from-application-variable-entity-to-flat-application-variable.util.ts index d7777cf19a..f2a4b63492 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-application-variable/utils/from-application-variable-entity-to-flat-application-variable.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-application-variable/utils/from-application-variable-entity-to-flat-application-variable.util.ts @@ -1,39 +1,26 @@ -import { isDefined } from 'twenty-shared/utils'; - -import { - FlatEntityMapsException, - FlatEntityMapsExceptionCode, -} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception'; +import { fromEntityToScalarEntity } from 'src/engine/metadata-modules/flat-entity/utils/from-entity-to-scalar-entity.util'; import { type FlatApplicationVariable } from 'src/engine/metadata-modules/flat-application-variable/types/flat-application-variable.type'; import { type FromEntityToFlatEntityArgs } from 'src/engine/workspace-cache/types/from-entity-to-flat-entity-args.type'; +import { resolveManyToOneRelationIdsToUniversalIdentifiers } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-many-to-one-relation-ids-to-universal-identifiers.util'; -export const fromApplicationVariableEntityToFlatApplicationVariable = ({ - entity: applicationVariableEntity, - applicationIdToUniversalIdentifierMap, -}: FromEntityToFlatEntityArgs<'applicationVariable'>): FlatApplicationVariable => { - const applicationUniversalIdentifier = - applicationIdToUniversalIdentifierMap.get( - applicationVariableEntity.applicationId, - ); +export const fromApplicationVariableEntityToFlatApplicationVariable = ( + args: FromEntityToFlatEntityArgs<'applicationVariable'>, +): FlatApplicationVariable => { + const { entity: applicationVariableEntity } = args; - if (!isDefined(applicationUniversalIdentifier)) { - throw new FlatEntityMapsException( - `Application with id ${applicationVariableEntity.applicationId} not found for applicationVariable ${applicationVariableEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } + const applicationVariableScalarEntity = fromEntityToScalarEntity({ + metadataName: 'applicationVariable', + entity: applicationVariableEntity, + }); + + const relationUniversalIdentifiers = + resolveManyToOneRelationIdsToUniversalIdentifiers({ + metadataName: 'applicationVariable', + ...args, + }); return { - id: applicationVariableEntity.id, - key: applicationVariableEntity.key, - value: applicationVariableEntity.value, - description: applicationVariableEntity.description, - isSecret: applicationVariableEntity.isSecret, - workspaceId: applicationVariableEntity.workspaceId, - universalIdentifier: applicationVariableEntity.universalIdentifier, - applicationId: applicationVariableEntity.applicationId, - createdAt: applicationVariableEntity.createdAt.toISOString(), - updatedAt: applicationVariableEntity.updatedAt.toISOString(), - applicationUniversalIdentifier, + ...applicationVariableScalarEntity, + ...relationUniversalIdentifiers, }; }; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-command-menu-item/utils/from-command-menu-item-entity-to-flat-command-menu-item.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-command-menu-item/utils/from-command-menu-item-entity-to-flat-command-menu-item.util.ts index 00d08f250d..80732f27e8 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-command-menu-item/utils/from-command-menu-item-entity-to-flat-command-menu-item.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-command-menu-item/utils/from-command-menu-item-entity-to-flat-command-menu-item.util.ts @@ -1,79 +1,30 @@ import { isDefined } from 'twenty-shared/utils'; -import { fromCommandMenuItemOverridesToUniversalOverrides } from 'src/engine/metadata-modules/flat-command-menu-item/utils/from-command-menu-item-overrides-to-universal-overrides.util'; import { type FlatCommandMenuItem } from 'src/engine/metadata-modules/flat-command-menu-item/types/flat-command-menu-item.type'; -import { - FlatEntityMapsException, - FlatEntityMapsExceptionCode, -} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception'; +import { fromCommandMenuItemOverridesToUniversalOverrides } from 'src/engine/metadata-modules/flat-command-menu-item/utils/from-command-menu-item-overrides-to-universal-overrides.util'; +import { fromEntityToScalarEntity } from 'src/engine/metadata-modules/flat-entity/utils/from-entity-to-scalar-entity.util'; import { type FromEntityToFlatEntityArgs } from 'src/engine/workspace-cache/types/from-entity-to-flat-entity-args.type'; +import { resolveManyToOneRelationIdsToUniversalIdentifiers } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-many-to-one-relation-ids-to-universal-identifiers.util'; -export const fromCommandMenuItemEntityToFlatCommandMenuItem = ({ - entity: commandMenuItemEntity, - applicationIdToUniversalIdentifierMap, - objectMetadataIdToUniversalIdentifierMap, - frontComponentIdToUniversalIdentifierMap, - pageLayoutIdToUniversalIdentifierMap, -}: FromEntityToFlatEntityArgs<'commandMenuItem'>): FlatCommandMenuItem => { - const applicationUniversalIdentifier = - applicationIdToUniversalIdentifierMap.get( - commandMenuItemEntity.applicationId, - ); +export const fromCommandMenuItemEntityToFlatCommandMenuItem = ( + args: FromEntityToFlatEntityArgs<'commandMenuItem'>, +): FlatCommandMenuItem => { + const { + entity: commandMenuItemEntity, + objectMetadataIdToUniversalIdentifierMap, + pageLayoutIdToUniversalIdentifierMap, + } = args; - if (!isDefined(applicationUniversalIdentifier)) { - throw new FlatEntityMapsException( - `Application with id ${commandMenuItemEntity.applicationId} not found for commandMenuItem ${commandMenuItemEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } + const commandMenuItemScalarEntity = fromEntityToScalarEntity({ + metadataName: 'commandMenuItem', + entity: commandMenuItemEntity, + }); - let availabilityObjectMetadataUniversalIdentifier: string | null = null; - - if (isDefined(commandMenuItemEntity.availabilityObjectMetadataId)) { - availabilityObjectMetadataUniversalIdentifier = - objectMetadataIdToUniversalIdentifierMap.get( - commandMenuItemEntity.availabilityObjectMetadataId, - ) ?? null; - - if (!isDefined(availabilityObjectMetadataUniversalIdentifier)) { - throw new FlatEntityMapsException( - `ObjectMetadata with id ${commandMenuItemEntity.availabilityObjectMetadataId} not found for commandMenuItem ${commandMenuItemEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - } - - let frontComponentUniversalIdentifier: string | null = null; - - if (isDefined(commandMenuItemEntity.frontComponentId)) { - frontComponentUniversalIdentifier = - frontComponentIdToUniversalIdentifierMap.get( - commandMenuItemEntity.frontComponentId, - ) ?? null; - - if (!isDefined(frontComponentUniversalIdentifier)) { - throw new FlatEntityMapsException( - `FrontComponent with id ${commandMenuItemEntity.frontComponentId} not found for commandMenuItem ${commandMenuItemEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - } - - let pageLayoutUniversalIdentifier: string | null = null; - - if (isDefined(commandMenuItemEntity.pageLayoutId)) { - pageLayoutUniversalIdentifier = - pageLayoutIdToUniversalIdentifierMap.get( - commandMenuItemEntity.pageLayoutId, - ) ?? null; - - if (!isDefined(pageLayoutUniversalIdentifier)) { - throw new FlatEntityMapsException( - `PageLayout with id ${commandMenuItemEntity.pageLayoutId} not found for commandMenuItem ${commandMenuItemEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - } + const relationUniversalIdentifiers = + resolveManyToOneRelationIdsToUniversalIdentifiers({ + metadataName: 'commandMenuItem', + ...args, + }); const universalOverrides = isDefined(commandMenuItemEntity.overrides) ? fromCommandMenuItemOverridesToUniversalOverrides({ @@ -89,35 +40,8 @@ export const fromCommandMenuItemEntityToFlatCommandMenuItem = ({ : null; return { - id: commandMenuItemEntity.id, - workflowVersionId: commandMenuItemEntity.workflowVersionId, - frontComponentId: commandMenuItemEntity.frontComponentId, - engineComponentKey: commandMenuItemEntity.engineComponentKey, - label: commandMenuItemEntity.label, - icon: commandMenuItemEntity.icon, - shortLabel: commandMenuItemEntity.shortLabel, - position: commandMenuItemEntity.position, - isPinned: commandMenuItemEntity.isPinned, - payload: commandMenuItemEntity.payload, - hotKeys: commandMenuItemEntity.hotKeys, - availabilityType: commandMenuItemEntity.availabilityType, - availabilityObjectMetadataId: - commandMenuItemEntity.availabilityObjectMetadataId, - workspaceId: commandMenuItemEntity.workspaceId, - universalIdentifier: commandMenuItemEntity.universalIdentifier, - applicationId: commandMenuItemEntity.applicationId, - createdAt: commandMenuItemEntity.createdAt.toISOString(), - updatedAt: commandMenuItemEntity.updatedAt.toISOString(), - applicationUniversalIdentifier, - conditionalAvailabilityExpression: - commandMenuItemEntity.conditionalAvailabilityExpression, - availabilityObjectMetadataUniversalIdentifier, - frontComponentUniversalIdentifier, - pageLayoutId: commandMenuItemEntity.pageLayoutId, - pageLayoutUniversalIdentifier, - isActive: commandMenuItemEntity.isActive, - isSystemSideEffect: commandMenuItemEntity.isSystemSideEffect, - overrides: commandMenuItemEntity.overrides, + ...commandMenuItemScalarEntity, + ...relationUniversalIdentifiers, universalOverrides, }; }; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-connection-provider/utils/from-connection-provider-entity-to-flat-connection-provider.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-connection-provider/utils/from-connection-provider-entity-to-flat-connection-provider.util.ts index d6c7adb56d..bcec0ea355 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-connection-provider/utils/from-connection-provider-entity-to-flat-connection-provider.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-connection-provider/utils/from-connection-provider-entity-to-flat-connection-provider.util.ts @@ -1,39 +1,26 @@ -import { isDefined } from 'twenty-shared/utils'; - -import { - FlatEntityMapsException, - FlatEntityMapsExceptionCode, -} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception'; +import { fromEntityToScalarEntity } from 'src/engine/metadata-modules/flat-entity/utils/from-entity-to-scalar-entity.util'; import { type FlatConnectionProvider } from 'src/engine/metadata-modules/flat-connection-provider/types/flat-connection-provider.type'; import { type FromEntityToFlatEntityArgs } from 'src/engine/workspace-cache/types/from-entity-to-flat-entity-args.type'; +import { resolveManyToOneRelationIdsToUniversalIdentifiers } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-many-to-one-relation-ids-to-universal-identifiers.util'; -export const fromConnectionProviderEntityToFlatConnectionProvider = ({ - entity: connectionProviderEntity, - applicationIdToUniversalIdentifierMap, -}: FromEntityToFlatEntityArgs<'connectionProvider'>): FlatConnectionProvider => { - const applicationUniversalIdentifier = - applicationIdToUniversalIdentifierMap.get( - connectionProviderEntity.applicationId, - ); +export const fromConnectionProviderEntityToFlatConnectionProvider = ( + args: FromEntityToFlatEntityArgs<'connectionProvider'>, +): FlatConnectionProvider => { + const { entity: connectionProviderEntity } = args; - if (!isDefined(applicationUniversalIdentifier)) { - throw new FlatEntityMapsException( - `Application with id ${connectionProviderEntity.applicationId} not found for connection provider ${connectionProviderEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } + const connectionProviderScalarEntity = fromEntityToScalarEntity({ + metadataName: 'connectionProvider', + entity: connectionProviderEntity, + }); + + const relationUniversalIdentifiers = + resolveManyToOneRelationIdsToUniversalIdentifiers({ + metadataName: 'connectionProvider', + ...args, + }); return { - id: connectionProviderEntity.id, - universalIdentifier: connectionProviderEntity.universalIdentifier, - applicationId: connectionProviderEntity.applicationId, - workspaceId: connectionProviderEntity.workspaceId, - name: connectionProviderEntity.name, - displayName: connectionProviderEntity.displayName, - type: connectionProviderEntity.type, - oauthConfig: connectionProviderEntity.oauthConfig, - createdAt: connectionProviderEntity.createdAt.toISOString(), - updatedAt: connectionProviderEntity.updatedAt.toISOString(), - applicationUniversalIdentifier, + ...connectionProviderScalarEntity, + ...relationUniversalIdentifiers, }; }; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-entity/utils/__tests__/from-entity-to-scalar-entity.util.spec.ts b/packages/twenty-server/src/engine/metadata-modules/flat-entity/utils/__tests__/from-entity-to-scalar-entity.util.spec.ts new file mode 100644 index 0000000000..c48e3c3f18 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-entity/utils/__tests__/from-entity-to-scalar-entity.util.spec.ts @@ -0,0 +1,183 @@ +import { type AllMetadataName } from 'twenty-shared/metadata'; + +import { type MetadataEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-entity.type'; +import { fromEntityToScalarEntity } from 'src/engine/metadata-modules/flat-entity/utils/from-entity-to-scalar-entity.util'; +import { type EntityWithRegroupedOneToManyRelations } from 'src/engine/workspace-cache/types/entity-with-regrouped-one-to-many-relations.type'; + +const buildArgs = ( + metadataName: T, + entity: Record, +): { + metadataName: T; + entity: EntityWithRegroupedOneToManyRelations>; +} => ({ + metadataName, + entity: entity as unknown as EntityWithRegroupedOneToManyRelations< + MetadataEntity + >, +}); + +describe('fromEntityToScalarEntity', () => { + it('should return the registered scalar properties along with the base columns', () => { + const result = fromEntityToScalarEntity( + buildArgs('permissionFlag', { + id: 'permission-flag-id-1', + workspaceId: 'workspace-id-1', + applicationId: 'app-id-1', + universalIdentifier: 'permission-flag-ui-1', + key: 'IMPERSONATE', + label: 'Impersonate', + description: 'Allows impersonation', + icon: 'IconUser', + permissionType: 'WORKSPACE', + createdAt: new Date('2024-01-01T00:00:00.000Z'), + updatedAt: new Date('2024-01-02T00:00:00.000Z'), + }), + ); + + expect(result).toEqual({ + id: 'permission-flag-id-1', + workspaceId: 'workspace-id-1', + applicationId: 'app-id-1', + universalIdentifier: 'permission-flag-ui-1', + key: 'IMPERSONATE', + label: 'Impersonate', + description: 'Allows impersonation', + icon: 'IconUser', + permissionType: 'WORKSPACE', + createdAt: '2024-01-01T00:00:00.000Z', + updatedAt: '2024-01-02T00:00:00.000Z', + }); + }); + + it('should drop relation objects, regrouped one-to-many arrays and any unregistered property', () => { + const result = fromEntityToScalarEntity( + buildArgs('permissionFlag', { + id: 'permission-flag-id-1', + workspaceId: 'workspace-id-1', + applicationId: 'app-id-1', + universalIdentifier: 'permission-flag-ui-1', + key: 'IMPERSONATE', + label: 'Impersonate', + description: 'Allows impersonation', + icon: 'IconUser', + permissionType: 'WORKSPACE', + createdAt: new Date('2024-01-01T00:00:00.000Z'), + updatedAt: new Date('2024-01-02T00:00:00.000Z'), + workspace: { id: 'workspace-id-1' }, + application: { id: 'app-id-1' }, + rolePermissionFlags: [{ id: 'role-permission-flag-id-1' }], + someTransientProperty: 'should-not-leak', + }), + ); + + expect(Object.keys(result).sort()).toEqual( + [ + 'id', + 'workspaceId', + 'applicationId', + 'universalIdentifier', + 'key', + 'label', + 'description', + 'icon', + 'permissionType', + 'createdAt', + 'updatedAt', + ].sort(), + ); + expect(result).not.toHaveProperty('workspace'); + expect(result).not.toHaveProperty('application'); + expect(result).not.toHaveProperty('rolePermissionFlags'); + expect(result).not.toHaveProperty('someTransientProperty'); + }); + + it('should keep many-to-one foreign key columns while dropping their relation objects', () => { + const result = fromEntityToScalarEntity( + buildArgs('viewSort', { + id: 'view-sort-id-1', + workspaceId: 'workspace-id-1', + applicationId: 'app-id-1', + universalIdentifier: 'view-sort-ui-1', + direction: 'ASC', + subFieldName: null, + fieldMetadataId: 'field-id-1', + viewId: 'view-id-1', + createdAt: new Date('2024-01-01T00:00:00.000Z'), + updatedAt: new Date('2024-01-02T00:00:00.000Z'), + deletedAt: null, + fieldMetadata: { id: 'field-id-1' }, + view: { id: 'view-id-1' }, + }), + ); + + expect(result.fieldMetadataId).toBe('field-id-1'); + expect(result.viewId).toBe('view-id-1'); + expect(result).not.toHaveProperty('fieldMetadata'); + expect(result).not.toHaveProperty('view'); + }); + + it('should serialize Date columns to ISO strings while leaving null dates untouched', () => { + const result = fromEntityToScalarEntity( + buildArgs('viewSort', { + id: 'view-sort-id-1', + workspaceId: 'workspace-id-1', + applicationId: 'app-id-1', + universalIdentifier: 'view-sort-ui-1', + direction: 'ASC', + subFieldName: null, + fieldMetadataId: 'field-id-1', + viewId: 'view-id-1', + createdAt: new Date('2024-01-01T00:00:00.000Z'), + updatedAt: new Date('2024-01-02T00:00:00.000Z'), + deletedAt: null, + }), + ); + + expect(result.createdAt).toBe('2024-01-01T00:00:00.000Z'); + expect(result.updatedAt).toBe('2024-01-02T00:00:00.000Z'); + expect(result.deletedAt).toBeNull(); + }); + + it('should normalize absent properties to null instead of undefined', () => { + const result = fromEntityToScalarEntity( + buildArgs('permissionFlag', { + id: 'permission-flag-id-1', + key: 'IMPERSONATE', + label: 'Impersonate', + description: 'Allows impersonation', + icon: 'IconUser', + permissionType: 'WORKSPACE', + createdAt: new Date('2024-01-01T00:00:00.000Z'), + updatedAt: new Date('2024-01-02T00:00:00.000Z'), + }), + ); + + expect(result).toHaveProperty('workspaceId', null); + expect(result).toHaveProperty('applicationId', null); + expect(result).toHaveProperty('universalIdentifier', null); + expect(Object.values(result)).not.toContain(undefined); + }); + + it('should keep explicit null values and normalize undefined ones to null', () => { + const result = fromEntityToScalarEntity( + buildArgs('viewSort', { + id: 'view-sort-id-1', + workspaceId: 'workspace-id-1', + applicationId: null, + universalIdentifier: 'view-sort-ui-1', + direction: 'ASC', + subFieldName: null, + fieldMetadataId: 'field-id-1', + viewId: 'view-id-1', + createdAt: new Date('2024-01-01T00:00:00.000Z'), + updatedAt: new Date('2024-01-02T00:00:00.000Z'), + deletedAt: undefined, + }), + ); + + expect(result).toHaveProperty('applicationId', null); + expect(result).toHaveProperty('subFieldName', null); + expect(result).toHaveProperty('deletedAt', null); + }); +}); diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-entity/utils/from-entity-to-scalar-entity.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-entity/utils/from-entity-to-scalar-entity.util.ts new file mode 100644 index 0000000000..f50b223269 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-entity/utils/from-entity-to-scalar-entity.util.ts @@ -0,0 +1,54 @@ +import { type AllMetadataName } from 'twenty-shared/metadata'; + +import { ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME } from 'src/engine/metadata-modules/flat-entity/constant/all-entity-properties-configuration-by-metadata-name.constant'; +import { type MetadataEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-entity.type'; +import { type ScalarFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/scalar-flat-entity.type'; +import { type EntityWithRegroupedOneToManyRelations } from 'src/engine/workspace-cache/types/entity-with-regrouped-one-to-many-relations.type'; + +const serializeScalarValue = (value: unknown): unknown => { + if (value instanceof Date) { + return value.toISOString(); + } + + return value ?? null; +}; + +const BASE_SCALAR_PROPERTY_NAMES = [ + 'id', + 'workspaceId', + 'applicationId', + 'universalIdentifier', +] as const; + +// Allow-list counterpart to removePropertiesFromRecord: rather than forwarding +// every entity property except the known relations (deny-list), this forwards only +// the scalar columns registered in +// ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME plus the always-present base +// columns, serialized into their flat (ScalarFlatEntity) shape. Anything unexpected +// on the entity instance is dropped, so a flat entity can never silently leak an +// unregistered property. Absent values are normalized to `null` so the output always +// has a stable key shape and never an `undefined` value. +export const fromEntityToScalarEntity = ({ + metadataName, + entity, +}: { + metadataName: T; + entity: EntityWithRegroupedOneToManyRelations>; +}): ScalarFlatEntity> => { + const propertiesConfiguration = + ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME[metadataName]; + const entityRecord = entity as Record; + + const scalarProperties: Record = {}; + + for (const propertyName of [ + ...BASE_SCALAR_PROPERTY_NAMES, + ...Object.keys(propertiesConfiguration), + ]) { + scalarProperties[propertyName] = serializeScalarValue( + entityRecord[propertyName], + ); + } + + return scalarProperties as ScalarFlatEntity>; +}; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-field-metadata-entity-to-flat-field-metadata.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-field-metadata-entity-to-flat-field-metadata.util.ts index 46e9e1f99d..c61fef38ae 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-field-metadata-entity-to-flat-field-metadata.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-field-metadata-entity-to-flat-field-metadata.util.ts @@ -1,80 +1,30 @@ import { FieldMetadataType } from 'twenty-shared/types'; -import { isDefined, removePropertiesFromRecord } from 'twenty-shared/utils'; +import { isDefined } from 'twenty-shared/utils'; import { isFieldMetadataSettingsOfType } from 'src/engine/metadata-modules/field-metadata/utils/is-field-metadata-settings-of-type.util'; -import { - FlatEntityMapsException, - FlatEntityMapsExceptionCode, -} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception'; -import { getMetadataEntityRelationProperties } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-entity-relation-properties.util'; +import { fromEntityToScalarEntity } from 'src/engine/metadata-modules/flat-entity/utils/from-entity-to-scalar-entity.util'; import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type'; import { type FromEntityToFlatEntityArgs } from 'src/engine/workspace-cache/types/from-entity-to-flat-entity-args.type'; +import { resolveManyToOneRelationIdsToUniversalIdentifiers } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-many-to-one-relation-ids-to-universal-identifiers.util'; -export const fromFieldMetadataEntityToFlatFieldMetadata = ({ - entity: fieldMetadataEntity, - fieldMetadataIdToUniversalIdentifierMap, - objectMetadataIdToUniversalIdentifierMap, - applicationIdToUniversalIdentifierMap, -}: FromEntityToFlatEntityArgs<'fieldMetadata'>): FlatFieldMetadata => { - const fieldMetadataWithoutRelations = removePropertiesFromRecord( - fieldMetadataEntity, - getMetadataEntityRelationProperties('fieldMetadata'), - ); - const applicationUniversalIdentifier = - applicationIdToUniversalIdentifierMap.get( - fieldMetadataEntity.applicationId, - ); +export const fromFieldMetadataEntityToFlatFieldMetadata = ( + args: FromEntityToFlatEntityArgs<'fieldMetadata'>, +): FlatFieldMetadata => { + const { + entity: fieldMetadataEntity, + fieldMetadataIdToUniversalIdentifierMap, + } = args; - if (!isDefined(applicationUniversalIdentifier)) { - throw new FlatEntityMapsException( - `Application with id ${fieldMetadataEntity.applicationId} not found when building flat field metadata for field ${fieldMetadataEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } + const fieldMetadataScalarEntity = fromEntityToScalarEntity({ + metadataName: 'fieldMetadata', + entity: fieldMetadataEntity, + }); - const objectMetadataUniversalIdentifier = - objectMetadataIdToUniversalIdentifierMap.get( - fieldMetadataEntity.objectMetadataId, - ); - - if (!isDefined(objectMetadataUniversalIdentifier)) { - throw new FlatEntityMapsException( - `Object metadata with id ${fieldMetadataEntity.objectMetadataId} not found when building flat field metadata for field ${fieldMetadataEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - - let relationTargetObjectMetadataUniversalIdentifier: string | null = null; - - if (isDefined(fieldMetadataEntity.relationTargetObjectMetadataId)) { - relationTargetObjectMetadataUniversalIdentifier = - objectMetadataIdToUniversalIdentifierMap.get( - fieldMetadataEntity.relationTargetObjectMetadataId, - ) ?? null; - - if (!isDefined(relationTargetObjectMetadataUniversalIdentifier)) { - throw new FlatEntityMapsException( - `Relation target object metadata with id ${fieldMetadataEntity.relationTargetObjectMetadataId} not found when building flat field metadata for field ${fieldMetadataEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - } - - let relationTargetFieldMetadataUniversalIdentifier: string | null = null; - - if (isDefined(fieldMetadataEntity.relationTargetFieldMetadataId)) { - relationTargetFieldMetadataUniversalIdentifier = - fieldMetadataIdToUniversalIdentifierMap.get( - fieldMetadataEntity.relationTargetFieldMetadataId, - ) ?? null; - - if (!isDefined(relationTargetFieldMetadataUniversalIdentifier)) { - throw new FlatEntityMapsException( - `Relation target field metadata with id ${fieldMetadataEntity.relationTargetFieldMetadataId} not found when building flat field metadata for field ${fieldMetadataEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - } + const relationUniversalIdentifiers = + resolveManyToOneRelationIdsToUniversalIdentifiers({ + metadataName: 'fieldMetadata', + ...args, + }); const settings = fieldMetadataEntity.settings; const isRelationSettings = @@ -94,10 +44,8 @@ export const fromFieldMetadataEntityToFlatFieldMetadata = ({ : settings; return { - ...fieldMetadataWithoutRelations, - universalIdentifier: fieldMetadataWithoutRelations.universalIdentifier, - createdAt: fieldMetadataWithoutRelations.createdAt.toISOString(), - updatedAt: fieldMetadataWithoutRelations.updatedAt.toISOString(), + ...fieldMetadataScalarEntity, + ...relationUniversalIdentifiers, kanbanAggregateOperationViewIds: fieldMetadataEntity.kanbanAggregateOperationViews.map(({ id }) => id), calendarViewIds: fieldMetadataEntity.calendarViews.map(({ id }) => id), @@ -108,10 +56,6 @@ export const fromFieldMetadataEntityToFlatFieldMetadata = ({ viewFilterIds: fieldMetadataEntity.viewFilters.map(({ id }) => id), fieldPermissionIds: fieldMetadataEntity.fieldPermissions?.map(({ id }) => id) ?? [], - applicationUniversalIdentifier, - objectMetadataUniversalIdentifier, - relationTargetObjectMetadataUniversalIdentifier, - relationTargetFieldMetadataUniversalIdentifier, viewFieldUniversalIdentifiers: fieldMetadataEntity.viewFields.map( ({ universalIdentifier }) => universalIdentifier, ), diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-field-permission/utils/from-field-permission-entity-to-flat-field-permission.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-field-permission/utils/from-field-permission-entity-to-flat-field-permission.util.ts index fd33aecdee..3990c93f82 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-field-permission/utils/from-field-permission-entity-to-flat-field-permission.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-field-permission/utils/from-field-permission-entity-to-flat-field-permission.util.ts @@ -1,81 +1,26 @@ -import { isDefined, removePropertiesFromRecord } from 'twenty-shared/utils'; - -import { - FlatEntityMapsException, - FlatEntityMapsExceptionCode, -} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception'; -import { getMetadataEntityRelationProperties } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-entity-relation-properties.util'; +import { fromEntityToScalarEntity } from 'src/engine/metadata-modules/flat-entity/utils/from-entity-to-scalar-entity.util'; import { type FlatFieldPermission } from 'src/engine/metadata-modules/flat-field-permission/types/flat-field-permission.type'; import { type FromEntityToFlatEntityArgs } from 'src/engine/workspace-cache/types/from-entity-to-flat-entity-args.type'; +import { resolveManyToOneRelationIdsToUniversalIdentifiers } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-many-to-one-relation-ids-to-universal-identifiers.util'; -export const fromFieldPermissionEntityToFlatFieldPermission = ({ - entity: fieldPermissionEntity, - applicationIdToUniversalIdentifierMap, - roleIdToUniversalIdentifierMap, - objectMetadataIdToUniversalIdentifierMap, - fieldMetadataIdToUniversalIdentifierMap, -}: FromEntityToFlatEntityArgs<'fieldPermission'>): FlatFieldPermission => { - const fieldPermissionEntityWithoutRelations = removePropertiesFromRecord( - fieldPermissionEntity, - getMetadataEntityRelationProperties('fieldPermission'), - ); +export const fromFieldPermissionEntityToFlatFieldPermission = ( + args: FromEntityToFlatEntityArgs<'fieldPermission'>, +): FlatFieldPermission => { + const { entity: fieldPermissionEntity } = args; - const applicationUniversalIdentifier = - applicationIdToUniversalIdentifierMap.get( - fieldPermissionEntity.applicationId, - ); + const fieldPermissionScalarEntity = fromEntityToScalarEntity({ + metadataName: 'fieldPermission', + entity: fieldPermissionEntity, + }); - if (!isDefined(applicationUniversalIdentifier)) { - throw new FlatEntityMapsException( - `Application with id ${fieldPermissionEntity.applicationId} not found for fieldPermission ${fieldPermissionEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - - const roleUniversalIdentifier = roleIdToUniversalIdentifierMap.get( - fieldPermissionEntity.roleId, - ); - - if (!isDefined(roleUniversalIdentifier)) { - throw new FlatEntityMapsException( - `Role with id ${fieldPermissionEntity.roleId} not found for fieldPermission ${fieldPermissionEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - - const objectMetadataUniversalIdentifier = - objectMetadataIdToUniversalIdentifierMap.get( - fieldPermissionEntity.objectMetadataId, - ); - - if (!isDefined(objectMetadataUniversalIdentifier)) { - throw new FlatEntityMapsException( - `ObjectMetadata with id ${fieldPermissionEntity.objectMetadataId} not found for fieldPermission ${fieldPermissionEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - - const fieldMetadataUniversalIdentifier = - fieldMetadataIdToUniversalIdentifierMap.get( - fieldPermissionEntity.fieldMetadataId, - ); - - if (!isDefined(fieldMetadataUniversalIdentifier)) { - throw new FlatEntityMapsException( - `FieldMetadata with id ${fieldPermissionEntity.fieldMetadataId} not found for fieldPermission ${fieldPermissionEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } + const relationUniversalIdentifiers = + resolveManyToOneRelationIdsToUniversalIdentifiers({ + metadataName: 'fieldPermission', + ...args, + }); return { - ...fieldPermissionEntityWithoutRelations, - createdAt: fieldPermissionEntity.createdAt.toISOString(), - updatedAt: fieldPermissionEntity.updatedAt.toISOString(), - universalIdentifier: - fieldPermissionEntityWithoutRelations.universalIdentifier, - applicationUniversalIdentifier, - roleUniversalIdentifier, - objectMetadataUniversalIdentifier, - fieldMetadataUniversalIdentifier, + ...fieldPermissionScalarEntity, + ...relationUniversalIdentifiers, }; }; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-front-component/utils/from-front-component-entity-to-flat-front-component.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-front-component/utils/from-front-component-entity-to-flat-front-component.util.ts index bd49aaaeae..f59906ac39 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-front-component/utils/from-front-component-entity-to-flat-front-component.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-front-component/utils/from-front-component-entity-to-flat-front-component.util.ts @@ -1,43 +1,26 @@ -import { isDefined } from 'twenty-shared/utils'; - -import { - FlatEntityMapsException, - FlatEntityMapsExceptionCode, -} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception'; +import { fromEntityToScalarEntity } from 'src/engine/metadata-modules/flat-entity/utils/from-entity-to-scalar-entity.util'; import { type FlatFrontComponent } from 'src/engine/metadata-modules/flat-front-component/types/flat-front-component.type'; import { type FromEntityToFlatEntityArgs } from 'src/engine/workspace-cache/types/from-entity-to-flat-entity-args.type'; +import { resolveManyToOneRelationIdsToUniversalIdentifiers } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-many-to-one-relation-ids-to-universal-identifiers.util'; -export const fromFrontComponentEntityToFlatFrontComponent = ({ - entity: frontComponentEntity, - applicationIdToUniversalIdentifierMap, -}: FromEntityToFlatEntityArgs<'frontComponent'>): FlatFrontComponent => { - const applicationUniversalIdentifier = - applicationIdToUniversalIdentifierMap.get( - frontComponentEntity.applicationId, - ); +export const fromFrontComponentEntityToFlatFrontComponent = ( + args: FromEntityToFlatEntityArgs<'frontComponent'>, +): FlatFrontComponent => { + const { entity: frontComponentEntity } = args; - if (!isDefined(applicationUniversalIdentifier)) { - throw new FlatEntityMapsException( - `Application with id ${frontComponentEntity.applicationId} not found for frontComponent ${frontComponentEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } + const frontComponentScalarEntity = fromEntityToScalarEntity({ + metadataName: 'frontComponent', + entity: frontComponentEntity, + }); + + const relationUniversalIdentifiers = + resolveManyToOneRelationIdsToUniversalIdentifiers({ + metadataName: 'frontComponent', + ...args, + }); return { - id: frontComponentEntity.id, - name: frontComponentEntity.name, - description: frontComponentEntity.description, - sourceComponentPath: frontComponentEntity.sourceComponentPath, - builtComponentPath: frontComponentEntity.builtComponentPath, - componentName: frontComponentEntity.componentName, - builtComponentChecksum: frontComponentEntity.builtComponentChecksum, - isHeadless: frontComponentEntity.isHeadless, - usesSdkClient: frontComponentEntity.usesSdkClient, - workspaceId: frontComponentEntity.workspaceId, - universalIdentifier: frontComponentEntity.universalIdentifier, - applicationId: frontComponentEntity.applicationId, - createdAt: frontComponentEntity.createdAt.toISOString(), - updatedAt: frontComponentEntity.updatedAt.toISOString(), - applicationUniversalIdentifier, + ...frontComponentScalarEntity, + ...relationUniversalIdentifiers, }; }; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-index-metadata/utils/from-index-metadata-entity-to-flat-index-metadata.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-index-metadata/utils/from-index-metadata-entity-to-flat-index-metadata.util.ts index 7f4b851aa7..66019d5a45 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-index-metadata/utils/from-index-metadata-entity-to-flat-index-metadata.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-index-metadata/utils/from-index-metadata-entity-to-flat-index-metadata.util.ts @@ -4,53 +4,38 @@ import { FlatEntityMapsException, FlatEntityMapsExceptionCode, } from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception'; -import { getMetadataEntityRelationProperties } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-entity-relation-properties.util'; +import { fromEntityToScalarEntity } from 'src/engine/metadata-modules/flat-entity/utils/from-entity-to-scalar-entity.util'; import { type FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type'; import { type FromEntityToFlatEntityArgs } from 'src/engine/workspace-cache/types/from-entity-to-flat-entity-args.type'; +import { resolveManyToOneRelationIdsToUniversalIdentifiers } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-many-to-one-relation-ids-to-universal-identifiers.util'; -export const fromIndexMetadataEntityToFlatIndexMetadata = ({ - entity: indexMetadataEntity, - applicationIdToUniversalIdentifierMap, - objectMetadataIdToUniversalIdentifierMap, - fieldMetadataIdToUniversalIdentifierMap, -}: FromEntityToFlatEntityArgs<'index'> & { - fieldMetadataIdToUniversalIdentifierMap: Map; -}): FlatIndexMetadata => { - const applicationUniversalIdentifier = - applicationIdToUniversalIdentifierMap.get( - indexMetadataEntity.applicationId, - ); +type FromIndexMetadataEntityToFlatIndexMetadataArgs = + FromEntityToFlatEntityArgs<'index'> & { + fieldMetadataIdToUniversalIdentifierMap: Map; + }; - if (!isDefined(applicationUniversalIdentifier)) { - throw new FlatEntityMapsException( - `Application with id ${indexMetadataEntity.applicationId} not found for index ${indexMetadataEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } +export const fromIndexMetadataEntityToFlatIndexMetadata = ( + args: FromIndexMetadataEntityToFlatIndexMetadataArgs, +): FlatIndexMetadata => { + const { + entity: indexMetadataEntity, + fieldMetadataIdToUniversalIdentifierMap, + } = args; - const objectMetadataUniversalIdentifier = - objectMetadataIdToUniversalIdentifierMap.get( - indexMetadataEntity.objectMetadataId, - ); + const indexMetadataScalarEntity = fromEntityToScalarEntity({ + metadataName: 'index', + entity: indexMetadataEntity, + }); - if (!isDefined(objectMetadataUniversalIdentifier)) { - throw new FlatEntityMapsException( - `ObjectMetadata with id ${indexMetadataEntity.objectMetadataId} not found for index ${indexMetadataEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - - const indexMetadataEntityWithoutRelations = removePropertiesFromRecord( - indexMetadataEntity, - getMetadataEntityRelationProperties('index'), - ); + const relationUniversalIdentifiers = + resolveManyToOneRelationIdsToUniversalIdentifiers({ + metadataName: 'index', + ...args, + }); return { - ...indexMetadataEntityWithoutRelations, - createdAt: indexMetadataEntity.createdAt.toISOString(), - updatedAt: indexMetadataEntity.updatedAt.toISOString(), - universalIdentifier: - indexMetadataEntityWithoutRelations.universalIdentifier, + ...indexMetadataScalarEntity, + ...relationUniversalIdentifiers, flatIndexFieldMetadatas: indexMetadataEntity.indexFieldMetadatas.map( (indexFieldMetadata) => ({ ...removePropertiesFromRecord(indexFieldMetadata, [ @@ -86,7 +71,5 @@ export const fromIndexMetadataEntityToFlatIndexMetadata = ({ fieldMetadataUniversalIdentifier, }; }), - applicationUniversalIdentifier, - objectMetadataUniversalIdentifier, }; }; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-navigation-menu-item/utils/from-navigation-menu-item-entity-to-flat-navigation-menu-item.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-navigation-menu-item/utils/from-navigation-menu-item-entity-to-flat-navigation-menu-item.util.ts index a4588391c7..e2552943a5 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-navigation-menu-item/utils/from-navigation-menu-item-entity-to-flat-navigation-menu-item.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-navigation-menu-item/utils/from-navigation-menu-item-entity-to-flat-navigation-menu-item.util.ts @@ -1,118 +1,26 @@ -import { isDefined } from 'twenty-shared/utils'; - -import { - FlatEntityMapsException, - FlatEntityMapsExceptionCode, -} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception'; +import { fromEntityToScalarEntity } from 'src/engine/metadata-modules/flat-entity/utils/from-entity-to-scalar-entity.util'; import { type FlatNavigationMenuItem } from 'src/engine/metadata-modules/flat-navigation-menu-item/types/flat-navigation-menu-item.type'; import { type FromEntityToFlatEntityArgs } from 'src/engine/workspace-cache/types/from-entity-to-flat-entity-args.type'; +import { resolveManyToOneRelationIdsToUniversalIdentifiers } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-many-to-one-relation-ids-to-universal-identifiers.util'; -export const fromNavigationMenuItemEntityToFlatNavigationMenuItem = ({ - entity: navigationMenuItemEntity, - applicationIdToUniversalIdentifierMap, - objectMetadataIdToUniversalIdentifierMap, - navigationMenuItemIdToUniversalIdentifierMap, - viewIdToUniversalIdentifierMap, - pageLayoutIdToUniversalIdentifierMap, -}: FromEntityToFlatEntityArgs<'navigationMenuItem'>): FlatNavigationMenuItem => { - const applicationUniversalIdentifier = - applicationIdToUniversalIdentifierMap.get( - navigationMenuItemEntity.applicationId, - ); +export const fromNavigationMenuItemEntityToFlatNavigationMenuItem = ( + args: FromEntityToFlatEntityArgs<'navigationMenuItem'>, +): FlatNavigationMenuItem => { + const { entity: navigationMenuItemEntity } = args; - if (!isDefined(applicationUniversalIdentifier)) { - throw new FlatEntityMapsException( - `Application with id ${navigationMenuItemEntity.applicationId} not found for navigationMenuItem ${navigationMenuItemEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } + const navigationMenuItemScalarEntity = fromEntityToScalarEntity({ + metadataName: 'navigationMenuItem', + entity: navigationMenuItemEntity, + }); - let targetObjectMetadataUniversalIdentifier: string | null = null; - - if (isDefined(navigationMenuItemEntity.targetObjectMetadataId)) { - targetObjectMetadataUniversalIdentifier = - objectMetadataIdToUniversalIdentifierMap.get( - navigationMenuItemEntity.targetObjectMetadataId, - ) ?? null; - - if (!isDefined(targetObjectMetadataUniversalIdentifier)) { - throw new FlatEntityMapsException( - `ObjectMetadata with id ${navigationMenuItemEntity.targetObjectMetadataId} not found for navigationMenuItem ${navigationMenuItemEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - } - - let folderUniversalIdentifier: string | null = null; - - if (isDefined(navigationMenuItemEntity.folderId)) { - folderUniversalIdentifier = - navigationMenuItemIdToUniversalIdentifierMap.get( - navigationMenuItemEntity.folderId, - ) ?? null; - - if (!isDefined(folderUniversalIdentifier)) { - throw new FlatEntityMapsException( - `NavigationMenuItem (folder) with id ${navigationMenuItemEntity.folderId} not found for navigationMenuItem ${navigationMenuItemEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - } - - let viewUniversalIdentifier: string | null = null; - - if (isDefined(navigationMenuItemEntity.viewId)) { - viewUniversalIdentifier = - viewIdToUniversalIdentifierMap.get(navigationMenuItemEntity.viewId) ?? - null; - - if (!isDefined(viewUniversalIdentifier)) { - throw new FlatEntityMapsException( - `View with id ${navigationMenuItemEntity.viewId} not found for navigationMenuItem ${navigationMenuItemEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - } - - let pageLayoutUniversalIdentifier: string | null = null; - - if (isDefined(navigationMenuItemEntity.pageLayoutId)) { - pageLayoutUniversalIdentifier = - pageLayoutIdToUniversalIdentifierMap.get( - navigationMenuItemEntity.pageLayoutId, - ) ?? null; - - if (!isDefined(pageLayoutUniversalIdentifier)) { - throw new FlatEntityMapsException( - `PageLayout with id ${navigationMenuItemEntity.pageLayoutId} not found for navigationMenuItem ${navigationMenuItemEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - } + const relationUniversalIdentifiers = + resolveManyToOneRelationIdsToUniversalIdentifiers({ + metadataName: 'navigationMenuItem', + ...args, + }); return { - id: navigationMenuItemEntity.id, - type: navigationMenuItemEntity.type, - userWorkspaceId: navigationMenuItemEntity.userWorkspaceId, - targetRecordId: navigationMenuItemEntity.targetRecordId, - targetObjectMetadataId: navigationMenuItemEntity.targetObjectMetadataId, - viewId: navigationMenuItemEntity.viewId, - folderId: navigationMenuItemEntity.folderId, - name: navigationMenuItemEntity.name, - link: navigationMenuItemEntity.link, - icon: navigationMenuItemEntity.icon, - color: navigationMenuItemEntity.color, - pageLayoutId: navigationMenuItemEntity.pageLayoutId, - position: navigationMenuItemEntity.position, - workspaceId: navigationMenuItemEntity.workspaceId, - universalIdentifier: navigationMenuItemEntity.universalIdentifier, - applicationId: navigationMenuItemEntity.applicationId, - createdAt: navigationMenuItemEntity.createdAt.toISOString(), - updatedAt: navigationMenuItemEntity.updatedAt.toISOString(), - applicationUniversalIdentifier, - targetObjectMetadataUniversalIdentifier, - folderUniversalIdentifier, - viewUniversalIdentifier, - pageLayoutUniversalIdentifier, + ...navigationMenuItemScalarEntity, + ...relationUniversalIdentifiers, }; }; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/from-object-metadata-entity-to-flat-object-metadata.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/from-object-metadata-entity-to-flat-object-metadata.util.ts index 0c12b796ac..3234cd77bc 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/from-object-metadata-entity-to-flat-object-metadata.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/from-object-metadata-entity-to-flat-object-metadata.util.ts @@ -1,40 +1,36 @@ -import { isDefined, removePropertiesFromRecord } from 'twenty-shared/utils'; +import { isDefined } from 'twenty-shared/utils'; -import { - FlatEntityMapsException, - FlatEntityMapsExceptionCode, -} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception'; -import { getMetadataEntityRelationProperties } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-entity-relation-properties.util'; +import { fromEntityToScalarEntity } from 'src/engine/metadata-modules/flat-entity/utils/from-entity-to-scalar-entity.util'; import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type'; import { type FromEntityToFlatEntityArgs } from 'src/engine/workspace-cache/types/from-entity-to-flat-entity-args.type'; +import { resolveManyToOneRelationIdsToUniversalIdentifiers } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-many-to-one-relation-ids-to-universal-identifiers.util'; type FromObjectMetadataEntityToFlatObjectMetadataArgs = FromEntityToFlatEntityArgs<'objectMetadata'> & { fieldMetadataIdToUniversalIdentifierMap: Map; }; -export const fromObjectMetadataEntityToFlatObjectMetadata = ({ - entity: objectMetadataEntity, - applicationIdToUniversalIdentifierMap, - fieldMetadataIdToUniversalIdentifierMap, -}: FromObjectMetadataEntityToFlatObjectMetadataArgs): FlatObjectMetadata => { - const objectMetadataEntityWithoutRelations = removePropertiesFromRecord( - objectMetadataEntity, - getMetadataEntityRelationProperties('objectMetadata'), - ); +export const fromObjectMetadataEntityToFlatObjectMetadata = ( + args: FromObjectMetadataEntityToFlatObjectMetadataArgs, +): FlatObjectMetadata => { + const { + entity: objectMetadataEntity, + fieldMetadataIdToUniversalIdentifierMap, + } = args; - const applicationUniversalIdentifier = - applicationIdToUniversalIdentifierMap.get( - objectMetadataEntity.applicationId, - ); + const objectMetadataScalarEntity = fromEntityToScalarEntity({ + metadataName: 'objectMetadata', + entity: objectMetadataEntity, + }); - if (!isDefined(applicationUniversalIdentifier)) { - throw new FlatEntityMapsException( - `Application with id ${objectMetadataEntity.applicationId} not found when building flat object metadata for object ${objectMetadataEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } + const relationUniversalIdentifiers = + resolveManyToOneRelationIdsToUniversalIdentifiers({ + metadataName: 'objectMetadata', + ...args, + }); + // labelIdentifier/imageIdentifier are soft field references (not modeled + // relations) resolved without throwing on missing identifiers let labelIdentifierFieldMetadataUniversalIdentifier: string | null = null; if (isDefined(objectMetadataEntity.labelIdentifierFieldMetadataId)) { @@ -70,11 +66,10 @@ export const fromObjectMetadataEntityToFlatObjectMetadata = ({ } return { - ...objectMetadataEntityWithoutRelations, - universalIdentifier: - objectMetadataEntityWithoutRelations.universalIdentifier, - createdAt: objectMetadataEntity.createdAt.toISOString(), - updatedAt: objectMetadataEntity.updatedAt.toISOString(), + ...objectMetadataScalarEntity, + ...relationUniversalIdentifiers, + labelIdentifierFieldMetadataUniversalIdentifier, + imageIdentifierFieldMetadataUniversalIdentifier, viewIds: objectMetadataEntity.views.map(({ id }) => id), indexMetadataIds: objectMetadataEntity.indexMetadatas.map(({ id }) => id), searchFieldMetadataIds: objectMetadataEntity.searchFieldMetadatas.map( @@ -86,9 +81,6 @@ export const fromObjectMetadataEntityToFlatObjectMetadata = ({ ), fieldPermissionIds: objectMetadataEntity.fieldPermissions?.map(({ id }) => id) ?? [], - applicationUniversalIdentifier, - labelIdentifierFieldMetadataUniversalIdentifier, - imageIdentifierFieldMetadataUniversalIdentifier, fieldUniversalIdentifiers: objectMetadataEntity.fields.map( ({ universalIdentifier }) => universalIdentifier, ), diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-object-permission/utils/from-object-permission-entity-to-flat-object-permission.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-object-permission/utils/from-object-permission-entity-to-flat-object-permission.util.ts index 43b2e056a5..bd8ca4770a 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-object-permission/utils/from-object-permission-entity-to-flat-object-permission.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-object-permission/utils/from-object-permission-entity-to-flat-object-permission.util.ts @@ -1,67 +1,26 @@ -import { isDefined, removePropertiesFromRecord } from 'twenty-shared/utils'; - -import { - FlatEntityMapsException, - FlatEntityMapsExceptionCode, -} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception'; -import { getMetadataEntityRelationProperties } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-entity-relation-properties.util'; +import { fromEntityToScalarEntity } from 'src/engine/metadata-modules/flat-entity/utils/from-entity-to-scalar-entity.util'; import { type FlatObjectPermission } from 'src/engine/metadata-modules/flat-object-permission/types/flat-object-permission.type'; import { type FromEntityToFlatEntityArgs } from 'src/engine/workspace-cache/types/from-entity-to-flat-entity-args.type'; +import { resolveManyToOneRelationIdsToUniversalIdentifiers } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-many-to-one-relation-ids-to-universal-identifiers.util'; -export const fromObjectPermissionEntityToFlatObjectPermission = ({ - entity: objectPermissionEntity, - applicationIdToUniversalIdentifierMap, - roleIdToUniversalIdentifierMap, - objectMetadataIdToUniversalIdentifierMap, -}: FromEntityToFlatEntityArgs<'objectPermission'>): FlatObjectPermission => { - const objectPermissionEntityWithoutRelations = removePropertiesFromRecord( - objectPermissionEntity, - getMetadataEntityRelationProperties('objectPermission'), - ); +export const fromObjectPermissionEntityToFlatObjectPermission = ( + args: FromEntityToFlatEntityArgs<'objectPermission'>, +): FlatObjectPermission => { + const { entity: objectPermissionEntity } = args; - const applicationUniversalIdentifier = - applicationIdToUniversalIdentifierMap.get( - objectPermissionEntity.applicationId, - ); + const objectPermissionScalarEntity = fromEntityToScalarEntity({ + metadataName: 'objectPermission', + entity: objectPermissionEntity, + }); - if (!isDefined(applicationUniversalIdentifier)) { - throw new FlatEntityMapsException( - `Application with id ${objectPermissionEntity.applicationId} not found for objectPermission ${objectPermissionEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - - const roleUniversalIdentifier = roleIdToUniversalIdentifierMap.get( - objectPermissionEntity.roleId, - ); - - if (!isDefined(roleUniversalIdentifier)) { - throw new FlatEntityMapsException( - `Role with id ${objectPermissionEntity.roleId} not found for objectPermission ${objectPermissionEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - - const objectMetadataUniversalIdentifier = - objectMetadataIdToUniversalIdentifierMap.get( - objectPermissionEntity.objectMetadataId, - ); - - if (!isDefined(objectMetadataUniversalIdentifier)) { - throw new FlatEntityMapsException( - `ObjectMetadata with id ${objectPermissionEntity.objectMetadataId} not found for objectPermission ${objectPermissionEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } + const relationUniversalIdentifiers = + resolveManyToOneRelationIdsToUniversalIdentifiers({ + metadataName: 'objectPermission', + ...args, + }); return { - ...objectPermissionEntityWithoutRelations, - createdAt: objectPermissionEntity.createdAt.toISOString(), - updatedAt: objectPermissionEntity.updatedAt.toISOString(), - universalIdentifier: - objectPermissionEntityWithoutRelations.universalIdentifier, - applicationUniversalIdentifier, - roleUniversalIdentifier, - objectMetadataUniversalIdentifier, + ...objectPermissionScalarEntity, + ...relationUniversalIdentifiers, }; }; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-page-layout-widget/utils/from-page-layout-widget-entity-to-flat-page-layout-widget.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-page-layout-widget/utils/from-page-layout-widget-entity-to-flat-page-layout-widget.util.ts index 82e059ecc3..051c8ca1a4 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-page-layout-widget/utils/from-page-layout-widget-entity-to-flat-page-layout-widget.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-page-layout-widget/utils/from-page-layout-widget-entity-to-flat-page-layout-widget.util.ts @@ -1,14 +1,11 @@ -import { isDefined, removePropertiesFromRecord } from 'twenty-shared/utils'; +import { isDefined } from 'twenty-shared/utils'; -import { - FlatEntityMapsException, - FlatEntityMapsExceptionCode, -} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception'; -import { getMetadataEntityRelationProperties } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-entity-relation-properties.util'; +import { fromEntityToScalarEntity } from 'src/engine/metadata-modules/flat-entity/utils/from-entity-to-scalar-entity.util'; import { type FlatPageLayoutWidget } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget.type'; import { fromPageLayoutWidgetConfigurationToUniversalConfiguration } from 'src/engine/metadata-modules/flat-page-layout-widget/utils/from-page-layout-widget-configuration-to-universal-configuration.util'; import { fromPageLayoutWidgetOverridesToUniversalOverrides } from 'src/engine/metadata-modules/flat-page-layout-widget/utils/from-page-layout-widget-overrides-to-universal-overrides.util'; import { type FromEntityToFlatEntityArgs } from 'src/engine/workspace-cache/types/from-entity-to-flat-entity-args.type'; +import { resolveManyToOneRelationIdsToUniversalIdentifiers } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-many-to-one-relation-ids-to-universal-identifiers.util'; type FromPageLayoutWidgetEntityToFlatPageLayoutWidgetArgs = FromEntityToFlatEntityArgs<'pageLayoutWidget'> & { @@ -18,64 +15,32 @@ type FromPageLayoutWidgetEntityToFlatPageLayoutWidgetArgs = viewUniversalIdentifierById?: Partial>; }; -export const fromPageLayoutWidgetEntityToFlatPageLayoutWidget = ({ - entity: pageLayoutWidgetEntity, - applicationIdToUniversalIdentifierMap, - pageLayoutTabIdToUniversalIdentifierMap, - objectMetadataIdToUniversalIdentifierMap, - fieldMetadataUniversalIdentifierById, - frontComponentUniversalIdentifierById, - viewFieldGroupUniversalIdentifierById, - viewUniversalIdentifierById, -}: FromPageLayoutWidgetEntityToFlatPageLayoutWidgetArgs): FlatPageLayoutWidget => { - const pageLayoutWidgetEntityWithoutRelations = removePropertiesFromRecord( - pageLayoutWidgetEntity, - getMetadataEntityRelationProperties('pageLayoutWidget'), - ); +export const fromPageLayoutWidgetEntityToFlatPageLayoutWidget = ( + args: FromPageLayoutWidgetEntityToFlatPageLayoutWidgetArgs, +): FlatPageLayoutWidget => { + const { + entity: pageLayoutWidgetEntity, + pageLayoutTabIdToUniversalIdentifierMap, + fieldMetadataUniversalIdentifierById, + frontComponentUniversalIdentifierById, + viewFieldGroupUniversalIdentifierById, + viewUniversalIdentifierById, + } = args; - const applicationUniversalIdentifier = - applicationIdToUniversalIdentifierMap.get( - pageLayoutWidgetEntity.applicationId, - ); + const pageLayoutWidgetScalarEntity = fromEntityToScalarEntity({ + metadataName: 'pageLayoutWidget', + entity: pageLayoutWidgetEntity, + }); - if (!isDefined(applicationUniversalIdentifier)) { - throw new FlatEntityMapsException( - `Application with id ${pageLayoutWidgetEntity.applicationId} not found for pageLayoutWidget ${pageLayoutWidgetEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - - const pageLayoutTabUniversalIdentifier = - pageLayoutTabIdToUniversalIdentifierMap.get( - pageLayoutWidgetEntity.pageLayoutTabId, - ); - - if (!isDefined(pageLayoutTabUniversalIdentifier)) { - throw new FlatEntityMapsException( - `PageLayoutTab with id ${pageLayoutWidgetEntity.pageLayoutTabId} not found for pageLayoutWidget ${pageLayoutWidgetEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - - let objectMetadataUniversalIdentifier: string | null = null; - - if (isDefined(pageLayoutWidgetEntity.objectMetadataId)) { - objectMetadataUniversalIdentifier = - objectMetadataIdToUniversalIdentifierMap.get( - pageLayoutWidgetEntity.objectMetadataId, - ) ?? null; - - if (!isDefined(objectMetadataUniversalIdentifier)) { - throw new FlatEntityMapsException( - `ObjectMetadata with id ${pageLayoutWidgetEntity.objectMetadataId} not found for pageLayoutWidget ${pageLayoutWidgetEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - } + const relationUniversalIdentifiers = + resolveManyToOneRelationIdsToUniversalIdentifiers({ + metadataName: 'pageLayoutWidget', + ...args, + }); const configurationWithUniversalIdentifiers = fromPageLayoutWidgetConfigurationToUniversalConfiguration({ - configuration: pageLayoutWidgetEntityWithoutRelations.configuration, + configuration: pageLayoutWidgetScalarEntity.configuration, fieldMetadataUniversalIdentifierById, frontComponentUniversalIdentifierById, viewFieldGroupUniversalIdentifierById, @@ -95,16 +60,8 @@ export const fromPageLayoutWidgetEntityToFlatPageLayoutWidget = ({ : null; return { - ...pageLayoutWidgetEntityWithoutRelations, - createdAt: pageLayoutWidgetEntity.createdAt.toISOString(), - updatedAt: pageLayoutWidgetEntity.updatedAt.toISOString(), - deletedAt: pageLayoutWidgetEntity.deletedAt?.toISOString() ?? null, - universalIdentifier: - pageLayoutWidgetEntityWithoutRelations.universalIdentifier, - applicationId: pageLayoutWidgetEntityWithoutRelations.applicationId, - applicationUniversalIdentifier, - pageLayoutTabUniversalIdentifier, - objectMetadataUniversalIdentifier, + ...pageLayoutWidgetScalarEntity, + ...relationUniversalIdentifiers, universalConfiguration: configurationWithUniversalIdentifiers, universalOverrides, }; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-permission-flag/utils/from-permission-flag-entity-to-flat-permission-flag.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-permission-flag/utils/from-permission-flag-entity-to-flat-permission-flag.util.ts index 6df8ffe6d6..d527e70594 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-permission-flag/utils/from-permission-flag-entity-to-flat-permission-flag.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-permission-flag/utils/from-permission-flag-entity-to-flat-permission-flag.util.ts @@ -1,40 +1,27 @@ -import { isDefined, removePropertiesFromRecord } from 'twenty-shared/utils'; - -import { - FlatEntityMapsException, - FlatEntityMapsExceptionCode, -} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception'; -import { getMetadataEntityRelationProperties } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-entity-relation-properties.util'; +import { fromEntityToScalarEntity } from 'src/engine/metadata-modules/flat-entity/utils/from-entity-to-scalar-entity.util'; import { type FlatPermissionFlag } from 'src/engine/metadata-modules/flat-permission-flag/types/flat-permission-flag.type'; import { type FromEntityToFlatEntityArgs } from 'src/engine/workspace-cache/types/from-entity-to-flat-entity-args.type'; +import { resolveManyToOneRelationIdsToUniversalIdentifiers } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-many-to-one-relation-ids-to-universal-identifiers.util'; -export const fromPermissionFlagEntityToFlatPermissionFlag = ({ - entity: permissionFlagEntity, - applicationIdToUniversalIdentifierMap, -}: FromEntityToFlatEntityArgs<'permissionFlag'>): FlatPermissionFlag => { - const entityWithoutRelations = removePropertiesFromRecord( - permissionFlagEntity, - getMetadataEntityRelationProperties('permissionFlag'), - ); +export const fromPermissionFlagEntityToFlatPermissionFlag = ( + args: FromEntityToFlatEntityArgs<'permissionFlag'>, +): FlatPermissionFlag => { + const { entity: permissionFlagEntity } = args; - const applicationUniversalIdentifier = - applicationIdToUniversalIdentifierMap.get( - permissionFlagEntity.applicationId, - ); + const permissionFlagScalarEntity = fromEntityToScalarEntity({ + metadataName: 'permissionFlag', + entity: permissionFlagEntity, + }); - if (!isDefined(applicationUniversalIdentifier)) { - throw new FlatEntityMapsException( - `Application with id ${permissionFlagEntity.applicationId} not found for permissionFlag ${permissionFlagEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } + const relationUniversalIdentifiers = + resolveManyToOneRelationIdsToUniversalIdentifiers({ + metadataName: 'permissionFlag', + ...args, + }); return { - ...entityWithoutRelations, - createdAt: permissionFlagEntity.createdAt.toISOString(), - updatedAt: permissionFlagEntity.updatedAt.toISOString(), - universalIdentifier: entityWithoutRelations.universalIdentifier, - applicationUniversalIdentifier, + ...permissionFlagScalarEntity, + ...relationUniversalIdentifiers, rolePermissionFlagIds: permissionFlagEntity.rolePermissionFlags.map( ({ id }) => id, ), 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 9c9b22b442..fe0a424b47 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,67 +1,26 @@ -import { isDefined, removePropertiesFromRecord } from 'twenty-shared/utils'; - -import { - FlatEntityMapsException, - FlatEntityMapsExceptionCode, -} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception'; -import { getMetadataEntityRelationProperties } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-entity-relation-properties.util'; +import { fromEntityToScalarEntity } from 'src/engine/metadata-modules/flat-entity/utils/from-entity-to-scalar-entity.util'; import { type FlatRolePermissionFlag } from 'src/engine/metadata-modules/flat-role-permission-flag/types/flat-role-permission-flag.type'; import { type FromEntityToFlatEntityArgs } from 'src/engine/workspace-cache/types/from-entity-to-flat-entity-args.type'; +import { resolveManyToOneRelationIdsToUniversalIdentifiers } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-many-to-one-relation-ids-to-universal-identifiers.util'; -export const fromRolePermissionFlagEntityToFlatRolePermissionFlag = ({ - entity: rolePermissionFlagEntity, - applicationIdToUniversalIdentifierMap, - permissionFlagIdToUniversalIdentifierMap, - roleIdToUniversalIdentifierMap, -}: FromEntityToFlatEntityArgs<'rolePermissionFlag'>): FlatRolePermissionFlag => { - const rolePermissionFlagEntityWithoutRelations = removePropertiesFromRecord( - rolePermissionFlagEntity, - getMetadataEntityRelationProperties('rolePermissionFlag'), - ); +export const fromRolePermissionFlagEntityToFlatRolePermissionFlag = ( + args: FromEntityToFlatEntityArgs<'rolePermissionFlag'>, +): FlatRolePermissionFlag => { + const { entity: rolePermissionFlagEntity } = args; - const applicationUniversalIdentifier = - applicationIdToUniversalIdentifierMap.get( - rolePermissionFlagEntity.applicationId, - ); + const rolePermissionFlagScalarEntity = fromEntityToScalarEntity({ + metadataName: 'rolePermissionFlag', + entity: rolePermissionFlagEntity, + }); - if (!isDefined(applicationUniversalIdentifier)) { - throw new FlatEntityMapsException( - `Application with id ${rolePermissionFlagEntity.applicationId} not found for rolePermissionFlag ${rolePermissionFlagEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - - const roleUniversalIdentifier = roleIdToUniversalIdentifierMap.get( - rolePermissionFlagEntity.roleId, - ); - - if (!isDefined(roleUniversalIdentifier)) { - throw new FlatEntityMapsException( - `Role with id ${rolePermissionFlagEntity.roleId} not found for rolePermissionFlag ${rolePermissionFlagEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - - const permissionFlagUniversalIdentifier = - permissionFlagIdToUniversalIdentifierMap.get( - rolePermissionFlagEntity.permissionFlagId, - ); - - if (!isDefined(permissionFlagUniversalIdentifier)) { - throw new FlatEntityMapsException( - `PermissionFlag with id ${rolePermissionFlagEntity.permissionFlagId} not found for rolePermissionFlag ${rolePermissionFlagEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } + const relationUniversalIdentifiers = + resolveManyToOneRelationIdsToUniversalIdentifiers({ + metadataName: 'rolePermissionFlag', + ...args, + }); return { - ...rolePermissionFlagEntityWithoutRelations, - createdAt: rolePermissionFlagEntity.createdAt.toISOString(), - updatedAt: rolePermissionFlagEntity.updatedAt.toISOString(), - universalIdentifier: - rolePermissionFlagEntityWithoutRelations.universalIdentifier, - applicationUniversalIdentifier, - permissionFlagUniversalIdentifier, - roleUniversalIdentifier, + ...rolePermissionFlagScalarEntity, + ...relationUniversalIdentifiers, }; }; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-role-target/utils/from-role-target-entity-to-flat-role-target.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-role-target/utils/from-role-target-entity-to-flat-role-target.util.ts index 6f8ecf0f33..fd8ea995cf 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-role-target/utils/from-role-target-entity-to-flat-role-target.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-role-target/utils/from-role-target-entity-to-flat-role-target.util.ts @@ -1,50 +1,26 @@ -import { isDefined } from 'twenty-shared/utils'; - -import { - FlatEntityMapsException, - FlatEntityMapsExceptionCode, -} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception'; +import { fromEntityToScalarEntity } from 'src/engine/metadata-modules/flat-entity/utils/from-entity-to-scalar-entity.util'; import { type FlatRoleTarget } from 'src/engine/metadata-modules/flat-role-target/types/flat-role-target.type'; import { type FromEntityToFlatEntityArgs } from 'src/engine/workspace-cache/types/from-entity-to-flat-entity-args.type'; +import { resolveManyToOneRelationIdsToUniversalIdentifiers } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-many-to-one-relation-ids-to-universal-identifiers.util'; -export const fromRoleTargetEntityToFlatRoleTarget = ({ - entity: roleTargetEntity, - applicationIdToUniversalIdentifierMap, - roleIdToUniversalIdentifierMap, -}: FromEntityToFlatEntityArgs<'roleTarget'>): FlatRoleTarget => { - const applicationUniversalIdentifier = - applicationIdToUniversalIdentifierMap.get(roleTargetEntity.applicationId); +export const fromRoleTargetEntityToFlatRoleTarget = ( + args: FromEntityToFlatEntityArgs<'roleTarget'>, +): FlatRoleTarget => { + const { entity: roleTargetEntity } = args; - if (!isDefined(applicationUniversalIdentifier)) { - throw new FlatEntityMapsException( - `Application with id ${roleTargetEntity.applicationId} not found when building flat role target for role target ${roleTargetEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } + const roleTargetScalarEntity = fromEntityToScalarEntity({ + metadataName: 'roleTarget', + entity: roleTargetEntity, + }); - const roleUniversalIdentifier = roleIdToUniversalIdentifierMap.get( - roleTargetEntity.roleId, - ); - - if (!isDefined(roleUniversalIdentifier)) { - throw new FlatEntityMapsException( - `Role with id ${roleTargetEntity.roleId} not found when building flat role target for role target ${roleTargetEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } + const relationUniversalIdentifiers = + resolveManyToOneRelationIdsToUniversalIdentifiers({ + metadataName: 'roleTarget', + ...args, + }); return { - id: roleTargetEntity.id, - workspaceId: roleTargetEntity.workspaceId, - roleId: roleTargetEntity.roleId, - userWorkspaceId: roleTargetEntity.userWorkspaceId, - agentId: roleTargetEntity.agentId, - apiKeyId: roleTargetEntity.apiKeyId, - applicationId: roleTargetEntity.applicationId, - universalIdentifier: roleTargetEntity.universalIdentifier, - createdAt: roleTargetEntity.createdAt.toISOString(), - updatedAt: roleTargetEntity.updatedAt.toISOString(), - applicationUniversalIdentifier, - roleUniversalIdentifier, + ...roleTargetScalarEntity, + ...relationUniversalIdentifiers, }; }; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-role/utils/from-role-entity-to-flat-role.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-role/utils/from-role-entity-to-flat-role.util.ts index 9b24bab625..15931f8f1c 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-role/utils/from-role-entity-to-flat-role.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-role/utils/from-role-entity-to-flat-role.util.ts @@ -1,14 +1,10 @@ -import { isDefined } from 'twenty-shared/utils'; - -import { - FlatEntityMapsException, - FlatEntityMapsExceptionCode, -} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception'; import { type MetadataEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-entity.type'; +import { fromEntityToScalarEntity } from 'src/engine/metadata-modules/flat-entity/utils/from-entity-to-scalar-entity.util'; import { type FlatRole } from 'src/engine/metadata-modules/flat-role/types/flat-role.type'; import { type EntityManyToOneIdByUniversalIdentifierMaps } from 'src/engine/workspace-cache/types/entity-many-to-one-id-by-universal-identifier-maps.type'; import { type EntityWithRegroupedOneToManyRelations } from 'src/engine/workspace-cache/types/entity-with-regrouped-one-to-many-relations.type'; import { type RegroupedEntity } from 'src/engine/workspace-cache/utils/regroup-entities-by-related-entity-id'; +import { resolveManyToOneRelationIdsToUniversalIdentifiers } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-many-to-one-relation-ids-to-universal-identifiers.util'; type FromRoleEntityToFlatRoleArgs = { entity: Omit< @@ -21,40 +17,25 @@ type FromRoleEntityToFlatRoleArgs = { }; } & EntityManyToOneIdByUniversalIdentifierMaps<'role'>; -export const fromRoleEntityToFlatRole = ({ - entity: roleEntity, - applicationIdToUniversalIdentifierMap, -}: FromRoleEntityToFlatRoleArgs): FlatRole => { - const applicationUniversalIdentifier = - applicationIdToUniversalIdentifierMap.get(roleEntity.applicationId); +export const fromRoleEntityToFlatRole = ( + args: FromRoleEntityToFlatRoleArgs, +): FlatRole => { + const { entity: roleEntity } = args; - if (!isDefined(applicationUniversalIdentifier)) { - throw new FlatEntityMapsException( - `Application with id ${roleEntity.applicationId} not found for role ${roleEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } + const roleScalarEntity = fromEntityToScalarEntity({ + metadataName: 'role', + entity: roleEntity, + }); + + const relationUniversalIdentifiers = + resolveManyToOneRelationIdsToUniversalIdentifiers({ + metadataName: 'role', + ...args, + }); return { - id: roleEntity.id, - label: roleEntity.label, - description: roleEntity.description, - icon: roleEntity.icon, - isEditable: roleEntity.isEditable, - canUpdateAllSettings: roleEntity.canUpdateAllSettings, - canAccessAllTools: roleEntity.canAccessAllTools, - canReadAllObjectRecords: roleEntity.canReadAllObjectRecords, - canUpdateAllObjectRecords: roleEntity.canUpdateAllObjectRecords, - canSoftDeleteAllObjectRecords: roleEntity.canSoftDeleteAllObjectRecords, - canDestroyAllObjectRecords: roleEntity.canDestroyAllObjectRecords, - canBeAssignedToUsers: roleEntity.canBeAssignedToUsers, - canBeAssignedToAgents: roleEntity.canBeAssignedToAgents, - canBeAssignedToApiKeys: roleEntity.canBeAssignedToApiKeys, - workspaceId: roleEntity.workspaceId, - createdAt: roleEntity.createdAt.toISOString(), - updatedAt: roleEntity.updatedAt.toISOString(), - universalIdentifier: roleEntity.universalIdentifier, - applicationId: roleEntity.applicationId, + ...roleScalarEntity, + ...relationUniversalIdentifiers, roleTargetIds: roleEntity.roleTargets.map(({ id }) => id), objectPermissionIds: roleEntity.objectPermissions.map(({ id }) => id), rolePermissionFlagIds: roleEntity.rolePermissionFlags.map(({ id }) => id), @@ -64,7 +45,6 @@ export const fromRoleEntityToFlatRole = ({ ), rowLevelPermissionPredicateGroupIds: roleEntity.rowLevelPermissionPredicateGroups.map(({ id }) => id), - applicationUniversalIdentifier, roleTargetUniversalIdentifiers: roleEntity.roleTargets.map( ({ universalIdentifier }) => universalIdentifier, ), diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/constants/row-level-permission-predicate-entity-relation-properties.constant.ts b/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/constants/row-level-permission-predicate-entity-relation-properties.constant.ts deleted file mode 100644 index c206c39bf1..0000000000 --- a/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/constants/row-level-permission-predicate-entity-relation-properties.constant.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* @license Enterprise */ - -export const ROW_LEVEL_PERMISSION_PREDICATE_ENTITY_RELATION_PROPERTIES = [ - 'workspace', - 'role', - 'fieldMetadata', - 'workspaceMemberFieldMetadata', - 'objectMetadata', - 'rowLevelPermissionPredicateGroup', - 'application', -] as const; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/constants/row-level-permission-predicate-group-entity-relation-properties.constant.ts b/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/constants/row-level-permission-predicate-group-entity-relation-properties.constant.ts deleted file mode 100644 index 2c6a838a74..0000000000 --- a/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/constants/row-level-permission-predicate-group-entity-relation-properties.constant.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* @license Enterprise */ - -export const ROW_LEVEL_PERMISSION_PREDICATE_GROUP_ENTITY_RELATION_PROPERTIES = [ - 'workspace', - 'role', - 'parentRowLevelPermissionPredicateGroup', - 'childRowLevelPermissionPredicateGroups', - 'rowLevelPermissionPredicates', - 'application', -] as const; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-row-level-permission-predicate-entity-to-flat-row-level-permission-predicate.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-row-level-permission-predicate-entity-to-flat-row-level-permission-predicate.util.ts index 835e65b0cc..1b8fb2e248 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-row-level-permission-predicate-entity-to-flat-row-level-permission-predicate.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-row-level-permission-predicate-entity-to-flat-row-level-permission-predicate.util.ts @@ -1,130 +1,29 @@ /* @license Enterprise */ -import { isDefined, removePropertiesFromRecord } from 'twenty-shared/utils'; - -import { - FlatEntityMapsException, - FlatEntityMapsExceptionCode, -} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception'; -import { ROW_LEVEL_PERMISSION_PREDICATE_ENTITY_RELATION_PROPERTIES } from 'src/engine/metadata-modules/flat-row-level-permission-predicate/constants/row-level-permission-predicate-entity-relation-properties.constant'; +import { fromEntityToScalarEntity } from 'src/engine/metadata-modules/flat-entity/utils/from-entity-to-scalar-entity.util'; import { type FlatRowLevelPermissionPredicate } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate.type'; import { type FromEntityToFlatEntityArgs } from 'src/engine/workspace-cache/types/from-entity-to-flat-entity-args.type'; +import { resolveManyToOneRelationIdsToUniversalIdentifiers } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-many-to-one-relation-ids-to-universal-identifiers.util'; export const fromRowLevelPermissionPredicateEntityToFlatRowLevelPermissionPredicate = - ({ - entity: rowLevelPermissionPredicateEntity, - applicationIdToUniversalIdentifierMap, - fieldMetadataIdToUniversalIdentifierMap, - objectMetadataIdToUniversalIdentifierMap, - roleIdToUniversalIdentifierMap, - rowLevelPermissionPredicateGroupIdToUniversalIdentifierMap, - }: FromEntityToFlatEntityArgs<'rowLevelPermissionPredicate'>): FlatRowLevelPermissionPredicate => { - const rowLevelPermissionPredicateEntityWithoutRelations = - removePropertiesFromRecord(rowLevelPermissionPredicateEntity, [ - ...ROW_LEVEL_PERMISSION_PREDICATE_ENTITY_RELATION_PROPERTIES, - ] as (typeof ROW_LEVEL_PERMISSION_PREDICATE_ENTITY_RELATION_PROPERTIES)[number][]); + ( + args: FromEntityToFlatEntityArgs<'rowLevelPermissionPredicate'>, + ): FlatRowLevelPermissionPredicate => { + const { entity: rowLevelPermissionPredicateEntity } = args; - const applicationUniversalIdentifier = - applicationIdToUniversalIdentifierMap.get( - rowLevelPermissionPredicateEntity.applicationId, - ); + const rowLevelPermissionPredicateScalarEntity = fromEntityToScalarEntity({ + metadataName: 'rowLevelPermissionPredicate', + entity: rowLevelPermissionPredicateEntity, + }); - if (!isDefined(applicationUniversalIdentifier)) { - throw new FlatEntityMapsException( - `Application with id ${rowLevelPermissionPredicateEntity.applicationId} not found for rowLevelPermissionPredicate ${rowLevelPermissionPredicateEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - - const fieldMetadataUniversalIdentifier = - fieldMetadataIdToUniversalIdentifierMap.get( - rowLevelPermissionPredicateEntity.fieldMetadataId, - ); - - if (!isDefined(fieldMetadataUniversalIdentifier)) { - throw new FlatEntityMapsException( - `FieldMetadata with id ${rowLevelPermissionPredicateEntity.fieldMetadataId} not found for rowLevelPermissionPredicate ${rowLevelPermissionPredicateEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - - const objectMetadataUniversalIdentifier = - objectMetadataIdToUniversalIdentifierMap.get( - rowLevelPermissionPredicateEntity.objectMetadataId, - ); - - if (!isDefined(objectMetadataUniversalIdentifier)) { - throw new FlatEntityMapsException( - `ObjectMetadata with id ${rowLevelPermissionPredicateEntity.objectMetadataId} not found for rowLevelPermissionPredicate ${rowLevelPermissionPredicateEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - - const roleUniversalIdentifier = roleIdToUniversalIdentifierMap.get( - rowLevelPermissionPredicateEntity.roleId, - ); - - if (!isDefined(roleUniversalIdentifier)) { - throw new FlatEntityMapsException( - `Role with id ${rowLevelPermissionPredicateEntity.roleId} not found for rowLevelPermissionPredicate ${rowLevelPermissionPredicateEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - - let workspaceMemberFieldMetadataUniversalIdentifier: string | null = null; - - if ( - isDefined( - rowLevelPermissionPredicateEntity.workspaceMemberFieldMetadataId, - ) - ) { - workspaceMemberFieldMetadataUniversalIdentifier = - fieldMetadataIdToUniversalIdentifierMap.get( - rowLevelPermissionPredicateEntity.workspaceMemberFieldMetadataId, - ) ?? null; - - if (!isDefined(workspaceMemberFieldMetadataUniversalIdentifier)) { - throw new FlatEntityMapsException( - `FieldMetadata with id ${rowLevelPermissionPredicateEntity.workspaceMemberFieldMetadataId} not found for rowLevelPermissionPredicate ${rowLevelPermissionPredicateEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - } - - let rowLevelPermissionPredicateGroupUniversalIdentifier: string | null = - null; - - if ( - isDefined( - rowLevelPermissionPredicateEntity.rowLevelPermissionPredicateGroupId, - ) - ) { - rowLevelPermissionPredicateGroupUniversalIdentifier = - rowLevelPermissionPredicateGroupIdToUniversalIdentifierMap.get( - rowLevelPermissionPredicateEntity.rowLevelPermissionPredicateGroupId, - ) ?? null; - - if (!isDefined(rowLevelPermissionPredicateGroupUniversalIdentifier)) { - throw new FlatEntityMapsException( - `RowLevelPermissionPredicateGroup with id ${rowLevelPermissionPredicateEntity.rowLevelPermissionPredicateGroupId} not found for rowLevelPermissionPredicate ${rowLevelPermissionPredicateEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - } + const relationUniversalIdentifiers = + resolveManyToOneRelationIdsToUniversalIdentifiers({ + metadataName: 'rowLevelPermissionPredicate', + ...args, + }); return { - ...rowLevelPermissionPredicateEntityWithoutRelations, - createdAt: rowLevelPermissionPredicateEntity.createdAt.toISOString(), - updatedAt: rowLevelPermissionPredicateEntity.updatedAt.toISOString(), - deletedAt: - rowLevelPermissionPredicateEntity.deletedAt?.toISOString() ?? null, - universalIdentifier: - rowLevelPermissionPredicateEntityWithoutRelations.universalIdentifier, - applicationUniversalIdentifier, - fieldMetadataUniversalIdentifier, - objectMetadataUniversalIdentifier, - roleUniversalIdentifier, - workspaceMemberFieldMetadataUniversalIdentifier, - rowLevelPermissionPredicateGroupUniversalIdentifier, + ...rowLevelPermissionPredicateScalarEntity, + ...relationUniversalIdentifiers, }; }; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-row-level-permission-predicate-group-entity-to-flat-row-level-permission-predicate-group.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-row-level-permission-predicate-group-entity-to-flat-row-level-permission-predicate-group.util.ts index df1bbae7bd..8f56d30552 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-row-level-permission-predicate-group-entity-to-flat-row-level-permission-predicate-group.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-row-level-permission-predicate/utils/from-row-level-permission-predicate-group-entity-to-flat-row-level-permission-predicate-group.util.ts @@ -1,103 +1,31 @@ /* @license Enterprise */ -import { isDefined, removePropertiesFromRecord } from 'twenty-shared/utils'; - -import { - FlatEntityMapsException, - FlatEntityMapsExceptionCode, -} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception'; -import { ROW_LEVEL_PERMISSION_PREDICATE_GROUP_ENTITY_RELATION_PROPERTIES } from 'src/engine/metadata-modules/flat-row-level-permission-predicate/constants/row-level-permission-predicate-group-entity-relation-properties.constant'; +import { fromEntityToScalarEntity } from 'src/engine/metadata-modules/flat-entity/utils/from-entity-to-scalar-entity.util'; import { type FlatRowLevelPermissionPredicateGroup } from 'src/engine/metadata-modules/row-level-permission-predicate/types/flat-row-level-permission-predicate-group.type'; import { type FromEntityToFlatEntityArgs } from 'src/engine/workspace-cache/types/from-entity-to-flat-entity-args.type'; - -type FromRowLevelPermissionPredicateGroupEntityToFlatRowLevelPermissionPredicateGroupArgs = - FromEntityToFlatEntityArgs<'rowLevelPermissionPredicateGroup'> & { - rowLevelPermissionPredicateGroupIdToUniversalIdentifierMap: Map< - string, - string - >; - }; +import { resolveManyToOneRelationIdsToUniversalIdentifiers } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-many-to-one-relation-ids-to-universal-identifiers.util'; export const fromRowLevelPermissionPredicateGroupEntityToFlatRowLevelPermissionPredicateGroup = - ({ - entity: rowLevelPermissionPredicateGroupEntity, - applicationIdToUniversalIdentifierMap, - objectMetadataIdToUniversalIdentifierMap, - roleIdToUniversalIdentifierMap, - rowLevelPermissionPredicateGroupIdToUniversalIdentifierMap, - }: FromRowLevelPermissionPredicateGroupEntityToFlatRowLevelPermissionPredicateGroupArgs): FlatRowLevelPermissionPredicateGroup => { - const rowLevelPermissionPredicateGroupEntityWithoutRelations = - removePropertiesFromRecord(rowLevelPermissionPredicateGroupEntity, [ - ...ROW_LEVEL_PERMISSION_PREDICATE_GROUP_ENTITY_RELATION_PROPERTIES, - ] as (typeof ROW_LEVEL_PERMISSION_PREDICATE_GROUP_ENTITY_RELATION_PROPERTIES)[number][]); + ( + args: FromEntityToFlatEntityArgs<'rowLevelPermissionPredicateGroup'>, + ): FlatRowLevelPermissionPredicateGroup => { + const { entity: rowLevelPermissionPredicateGroupEntity } = args; - const applicationUniversalIdentifier = - applicationIdToUniversalIdentifierMap.get( - rowLevelPermissionPredicateGroupEntity.applicationId, - ); + const rowLevelPermissionPredicateGroupScalarEntity = + fromEntityToScalarEntity({ + metadataName: 'rowLevelPermissionPredicateGroup', + entity: rowLevelPermissionPredicateGroupEntity, + }); - if (!isDefined(applicationUniversalIdentifier)) { - throw new FlatEntityMapsException( - `Application with id ${rowLevelPermissionPredicateGroupEntity.applicationId} not found for rowLevelPermissionPredicateGroup ${rowLevelPermissionPredicateGroupEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - - const objectMetadataUniversalIdentifier = - objectMetadataIdToUniversalIdentifierMap.get( - rowLevelPermissionPredicateGroupEntity.objectMetadataId, - ); - - if (!isDefined(objectMetadataUniversalIdentifier)) { - throw new FlatEntityMapsException( - `ObjectMetadata with id ${rowLevelPermissionPredicateGroupEntity.objectMetadataId} not found for rowLevelPermissionPredicateGroup ${rowLevelPermissionPredicateGroupEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - - const roleUniversalIdentifier = roleIdToUniversalIdentifierMap.get( - rowLevelPermissionPredicateGroupEntity.roleId, - ); - - if (!isDefined(roleUniversalIdentifier)) { - throw new FlatEntityMapsException( - `Role with id ${rowLevelPermissionPredicateGroupEntity.roleId} not found for rowLevelPermissionPredicateGroup ${rowLevelPermissionPredicateGroupEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - - let parentRowLevelPermissionPredicateGroupUniversalIdentifier: - | string - | null = null; - - if ( - isDefined( - rowLevelPermissionPredicateGroupEntity.parentRowLevelPermissionPredicateGroupId, - ) - ) { - parentRowLevelPermissionPredicateGroupUniversalIdentifier = - rowLevelPermissionPredicateGroupIdToUniversalIdentifierMap.get( - rowLevelPermissionPredicateGroupEntity.parentRowLevelPermissionPredicateGroupId, - ) ?? null; - - if ( - !isDefined(parentRowLevelPermissionPredicateGroupUniversalIdentifier) - ) { - throw new FlatEntityMapsException( - `RowLevelPermissionPredicateGroup with id ${rowLevelPermissionPredicateGroupEntity.parentRowLevelPermissionPredicateGroupId} not found for rowLevelPermissionPredicateGroup ${rowLevelPermissionPredicateGroupEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - } + const relationUniversalIdentifiers = + resolveManyToOneRelationIdsToUniversalIdentifiers({ + metadataName: 'rowLevelPermissionPredicateGroup', + ...args, + }); return { - ...rowLevelPermissionPredicateGroupEntityWithoutRelations, - createdAt: rowLevelPermissionPredicateGroupEntity.createdAt.toISOString(), - updatedAt: rowLevelPermissionPredicateGroupEntity.updatedAt.toISOString(), - deletedAt: - rowLevelPermissionPredicateGroupEntity.deletedAt?.toISOString() ?? null, - universalIdentifier: - rowLevelPermissionPredicateGroupEntityWithoutRelations.universalIdentifier, + ...rowLevelPermissionPredicateGroupScalarEntity, + ...relationUniversalIdentifiers, childRowLevelPermissionPredicateGroupIds: ( rowLevelPermissionPredicateGroupEntity.childRowLevelPermissionPredicateGroups ?? [] @@ -106,10 +34,6 @@ export const fromRowLevelPermissionPredicateGroupEntityToFlatRowLevelPermissionP rowLevelPermissionPredicateGroupEntity.rowLevelPermissionPredicates ?? [] ).map(({ id }) => id), - applicationUniversalIdentifier, - objectMetadataUniversalIdentifier, - roleUniversalIdentifier, - parentRowLevelPermissionPredicateGroupUniversalIdentifier, childRowLevelPermissionPredicateGroupUniversalIdentifiers: ( rowLevelPermissionPredicateGroupEntity.childRowLevelPermissionPredicateGroups ?? [] diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-skill/utils/from-skill-entity-to-flat-skill.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-skill/utils/from-skill-entity-to-flat-skill.util.ts index e02a54113b..7bbc8ba7a3 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-skill/utils/from-skill-entity-to-flat-skill.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-skill/utils/from-skill-entity-to-flat-skill.util.ts @@ -1,40 +1,26 @@ -import { isDefined } from 'twenty-shared/utils'; - -import { - FlatEntityMapsException, - FlatEntityMapsExceptionCode, -} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception'; +import { fromEntityToScalarEntity } from 'src/engine/metadata-modules/flat-entity/utils/from-entity-to-scalar-entity.util'; import { type FlatSkill } from 'src/engine/metadata-modules/flat-skill/types/flat-skill.type'; import { type FromEntityToFlatEntityArgs } from 'src/engine/workspace-cache/types/from-entity-to-flat-entity-args.type'; +import { resolveManyToOneRelationIdsToUniversalIdentifiers } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-many-to-one-relation-ids-to-universal-identifiers.util'; -export const fromSkillEntityToFlatSkill = ({ - entity: skillEntity, - applicationIdToUniversalIdentifierMap, -}: FromEntityToFlatEntityArgs<'skill'>): FlatSkill => { - const applicationUniversalIdentifier = - applicationIdToUniversalIdentifierMap.get(skillEntity.applicationId); +export const fromSkillEntityToFlatSkill = ( + args: FromEntityToFlatEntityArgs<'skill'>, +): FlatSkill => { + const { entity: skillEntity } = args; - if (!isDefined(applicationUniversalIdentifier)) { - throw new FlatEntityMapsException( - `Application with id ${skillEntity.applicationId} not found for skill ${skillEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } + const skillScalarEntity = fromEntityToScalarEntity({ + metadataName: 'skill', + entity: skillEntity, + }); + + const relationUniversalIdentifiers = + resolveManyToOneRelationIdsToUniversalIdentifiers({ + metadataName: 'skill', + ...args, + }); return { - createdAt: skillEntity.createdAt.toISOString(), - updatedAt: skillEntity.updatedAt.toISOString(), - id: skillEntity.id, - name: skillEntity.name, - label: skillEntity.label, - icon: skillEntity.icon, - description: skillEntity.description, - content: skillEntity.content, - workspaceId: skillEntity.workspaceId, - isCustom: skillEntity.isCustom, - isActive: skillEntity.isActive, - universalIdentifier: skillEntity.universalIdentifier, - applicationId: skillEntity.applicationId, - applicationUniversalIdentifier, + ...skillScalarEntity, + ...relationUniversalIdentifiers, }; }; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-view-field-group/utils/from-view-field-group-entity-to-flat-view-field-group.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-view-field-group/utils/from-view-field-group-entity-to-flat-view-field-group.util.ts index 01fc5c8644..8292ccded1 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-view-field-group/utils/from-view-field-group-entity-to-flat-view-field-group.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-view-field-group/utils/from-view-field-group-entity-to-flat-view-field-group.util.ts @@ -1,55 +1,27 @@ -import { isDefined, removePropertiesFromRecord } from 'twenty-shared/utils'; - -import { - FlatEntityMapsException, - FlatEntityMapsExceptionCode, -} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception'; -import { getMetadataEntityRelationProperties } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-entity-relation-properties.util'; +import { fromEntityToScalarEntity } from 'src/engine/metadata-modules/flat-entity/utils/from-entity-to-scalar-entity.util'; import { type FlatViewFieldGroup } from 'src/engine/metadata-modules/flat-view-field-group/types/flat-view-field-group.type'; import { type FromEntityToFlatEntityArgs } from 'src/engine/workspace-cache/types/from-entity-to-flat-entity-args.type'; +import { resolveManyToOneRelationIdsToUniversalIdentifiers } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-many-to-one-relation-ids-to-universal-identifiers.util'; -export const fromViewFieldGroupEntityToFlatViewFieldGroup = ({ - entity: viewFieldGroupEntity, - applicationIdToUniversalIdentifierMap, - viewIdToUniversalIdentifierMap, -}: FromEntityToFlatEntityArgs<'viewFieldGroup'>): FlatViewFieldGroup => { - const viewFieldGroupEntityWithoutRelations = removePropertiesFromRecord( - viewFieldGroupEntity, - getMetadataEntityRelationProperties('viewFieldGroup'), - ); +export const fromViewFieldGroupEntityToFlatViewFieldGroup = ( + args: FromEntityToFlatEntityArgs<'viewFieldGroup'>, +): FlatViewFieldGroup => { + const { entity: viewFieldGroupEntity } = args; - const applicationUniversalIdentifier = - applicationIdToUniversalIdentifierMap.get( - viewFieldGroupEntity.applicationId, - ); + const viewFieldGroupScalarEntity = fromEntityToScalarEntity({ + metadataName: 'viewFieldGroup', + entity: viewFieldGroupEntity, + }); - if (!isDefined(applicationUniversalIdentifier)) { - throw new FlatEntityMapsException( - `Application with id ${viewFieldGroupEntity.applicationId} not found for viewFieldGroup ${viewFieldGroupEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - - const viewUniversalIdentifier = viewIdToUniversalIdentifierMap.get( - viewFieldGroupEntity.viewId, - ); - - if (!isDefined(viewUniversalIdentifier)) { - throw new FlatEntityMapsException( - `View with id ${viewFieldGroupEntity.viewId} not found for viewFieldGroup ${viewFieldGroupEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } + const relationUniversalIdentifiers = + resolveManyToOneRelationIdsToUniversalIdentifiers({ + metadataName: 'viewFieldGroup', + ...args, + }); return { - ...viewFieldGroupEntityWithoutRelations, - createdAt: viewFieldGroupEntity.createdAt.toISOString(), - updatedAt: viewFieldGroupEntity.updatedAt.toISOString(), - deletedAt: viewFieldGroupEntity.deletedAt?.toISOString() ?? null, - universalIdentifier: - viewFieldGroupEntityWithoutRelations.universalIdentifier, - applicationUniversalIdentifier, - viewUniversalIdentifier, + ...viewFieldGroupScalarEntity, + ...relationUniversalIdentifiers, viewFieldIds: viewFieldGroupEntity.viewFields?.map((viewField) => viewField.id) ?? [], viewFieldUniversalIdentifiers: diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-view-field/utils/from-view-field-entity-to-flat-view-field.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-view-field/utils/from-view-field-entity-to-flat-view-field.util.ts index 8125c373dd..73bba4d7f1 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-view-field/utils/from-view-field-entity-to-flat-view-field.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-view-field/utils/from-view-field-entity-to-flat-view-field.util.ts @@ -1,74 +1,27 @@ -import { isDefined, removePropertiesFromRecord } from 'twenty-shared/utils'; +import { isDefined } from 'twenty-shared/utils'; -import { - FlatEntityMapsException, - FlatEntityMapsExceptionCode, -} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception'; -import { getMetadataEntityRelationProperties } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-entity-relation-properties.util'; +import { fromEntityToScalarEntity } from 'src/engine/metadata-modules/flat-entity/utils/from-entity-to-scalar-entity.util'; import { type FlatViewField } from 'src/engine/metadata-modules/flat-view-field/types/flat-view-field.type'; import { fromViewFieldOverridesToUniversalOverrides } from 'src/engine/metadata-modules/flat-view-field/utils/from-view-field-overrides-to-universal-overrides.util'; import { type FromEntityToFlatEntityArgs } from 'src/engine/workspace-cache/types/from-entity-to-flat-entity-args.type'; +import { resolveManyToOneRelationIdsToUniversalIdentifiers } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-many-to-one-relation-ids-to-universal-identifiers.util'; -export const fromViewFieldEntityToFlatViewField = ({ - entity: viewFieldEntity, - applicationIdToUniversalIdentifierMap, - fieldMetadataIdToUniversalIdentifierMap, - viewIdToUniversalIdentifierMap, - viewFieldGroupIdToUniversalIdentifierMap, -}: FromEntityToFlatEntityArgs<'viewField'>): FlatViewField => { - const viewFieldEntityWithoutRelations = removePropertiesFromRecord( - viewFieldEntity, - getMetadataEntityRelationProperties('viewField'), - ); +export const fromViewFieldEntityToFlatViewField = ( + args: FromEntityToFlatEntityArgs<'viewField'>, +): FlatViewField => { + const { entity: viewFieldEntity, viewFieldGroupIdToUniversalIdentifierMap } = + args; - const applicationUniversalIdentifier = - applicationIdToUniversalIdentifierMap.get(viewFieldEntity.applicationId); + const viewFieldScalarEntity = fromEntityToScalarEntity({ + metadataName: 'viewField', + entity: viewFieldEntity, + }); - if (!isDefined(applicationUniversalIdentifier)) { - throw new FlatEntityMapsException( - `Application with id ${viewFieldEntity.applicationId} not found for viewField ${viewFieldEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - - const fieldMetadataUniversalIdentifier = - fieldMetadataIdToUniversalIdentifierMap.get( - viewFieldEntity.fieldMetadataId, - ); - - if (!isDefined(fieldMetadataUniversalIdentifier)) { - throw new FlatEntityMapsException( - `FieldMetadata with id ${viewFieldEntity.fieldMetadataId} not found for viewField ${viewFieldEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - - const viewUniversalIdentifier = viewIdToUniversalIdentifierMap.get( - viewFieldEntity.viewId, - ); - - if (!isDefined(viewUniversalIdentifier)) { - throw new FlatEntityMapsException( - `View with id ${viewFieldEntity.viewId} not found for viewField ${viewFieldEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - - let viewFieldGroupUniversalIdentifier: string | null = null; - - if (isDefined(viewFieldEntity.viewFieldGroupId)) { - viewFieldGroupUniversalIdentifier = - viewFieldGroupIdToUniversalIdentifierMap.get( - viewFieldEntity.viewFieldGroupId, - ) ?? null; - - if (!isDefined(viewFieldGroupUniversalIdentifier)) { - throw new FlatEntityMapsException( - `ViewFieldGroup with id ${viewFieldEntity.viewFieldGroupId} not found for viewField ${viewFieldEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - } + const relationUniversalIdentifiers = + resolveManyToOneRelationIdsToUniversalIdentifiers({ + metadataName: 'viewField', + ...args, + }); const viewFieldGroupUniversalIdentifierById = Object.fromEntries( viewFieldGroupIdToUniversalIdentifierMap.entries(), @@ -83,15 +36,8 @@ export const fromViewFieldEntityToFlatViewField = ({ : null; return { - ...viewFieldEntityWithoutRelations, - createdAt: viewFieldEntity.createdAt.toISOString(), - updatedAt: viewFieldEntity.updatedAt.toISOString(), - deletedAt: viewFieldEntity.deletedAt?.toISOString() ?? null, - universalIdentifier: viewFieldEntityWithoutRelations.universalIdentifier, - applicationUniversalIdentifier, - fieldMetadataUniversalIdentifier, - viewUniversalIdentifier, - viewFieldGroupUniversalIdentifier, + ...viewFieldScalarEntity, + ...relationUniversalIdentifiers, universalOverrides, }; }; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-view-filter-group/utils/from-view-filter-group-entity-to-flat-view-filter-group.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-view-filter-group/utils/from-view-filter-group-entity-to-flat-view-filter-group.util.ts index cb349c0ff4..029d829eb0 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-view-filter-group/utils/from-view-filter-group-entity-to-flat-view-filter-group.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-view-filter-group/utils/from-view-filter-group-entity-to-flat-view-filter-group.util.ts @@ -1,81 +1,30 @@ -import { isDefined, removePropertiesFromRecord } from 'twenty-shared/utils'; - -import { - FlatEntityMapsException, - FlatEntityMapsExceptionCode, -} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception'; -import { getMetadataEntityRelationProperties } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-entity-relation-properties.util'; +import { fromEntityToScalarEntity } from 'src/engine/metadata-modules/flat-entity/utils/from-entity-to-scalar-entity.util'; import { type FlatViewFilterGroup } from 'src/engine/metadata-modules/flat-view-filter-group/types/flat-view-filter-group.type'; import { type FromEntityToFlatEntityArgs } from 'src/engine/workspace-cache/types/from-entity-to-flat-entity-args.type'; +import { resolveManyToOneRelationIdsToUniversalIdentifiers } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-many-to-one-relation-ids-to-universal-identifiers.util'; -type FromViewFilterGroupEntityToFlatViewFilterGroupArgs = - FromEntityToFlatEntityArgs<'viewFilterGroup'> & { - viewFilterGroupIdToUniversalIdentifierMap: Map; - }; +export const fromViewFilterGroupEntityToFlatViewFilterGroup = ( + args: FromEntityToFlatEntityArgs<'viewFilterGroup'>, +): FlatViewFilterGroup => { + const { entity: viewFilterGroupEntity } = args; -export const fromViewFilterGroupEntityToFlatViewFilterGroup = ({ - entity: viewFilterGroupEntity, - applicationIdToUniversalIdentifierMap, - viewFilterGroupIdToUniversalIdentifierMap, - viewIdToUniversalIdentifierMap, -}: FromViewFilterGroupEntityToFlatViewFilterGroupArgs): FlatViewFilterGroup => { - const viewFilterGroupEntityWithoutRelations = removePropertiesFromRecord( - viewFilterGroupEntity, - getMetadataEntityRelationProperties('viewFilterGroup'), - ); + const viewFilterGroupScalarEntity = fromEntityToScalarEntity({ + metadataName: 'viewFilterGroup', + entity: viewFilterGroupEntity, + }); - const applicationUniversalIdentifier = - applicationIdToUniversalIdentifierMap.get( - viewFilterGroupEntity.applicationId, - ); - - if (!isDefined(applicationUniversalIdentifier)) { - throw new FlatEntityMapsException( - `Application with id ${viewFilterGroupEntity.applicationId} not found for viewFilterGroup ${viewFilterGroupEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - - let parentViewFilterGroupUniversalIdentifier: string | null = null; - - if (isDefined(viewFilterGroupEntity.parentViewFilterGroupId)) { - parentViewFilterGroupUniversalIdentifier = - viewFilterGroupIdToUniversalIdentifierMap.get( - viewFilterGroupEntity.parentViewFilterGroupId, - ) ?? null; - - if (!isDefined(parentViewFilterGroupUniversalIdentifier)) { - throw new FlatEntityMapsException( - `ViewFilterGroup with id ${viewFilterGroupEntity.parentViewFilterGroupId} not found for viewFilterGroup ${viewFilterGroupEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - } - - const viewUniversalIdentifier = viewIdToUniversalIdentifierMap.get( - viewFilterGroupEntity.viewId, - ); - - if (!isDefined(viewUniversalIdentifier)) { - throw new FlatEntityMapsException( - `View with id ${viewFilterGroupEntity.viewId} not found for viewFilterGroup ${viewFilterGroupEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } + const relationUniversalIdentifiers = + resolveManyToOneRelationIdsToUniversalIdentifiers({ + metadataName: 'viewFilterGroup', + ...args, + }); return { - ...viewFilterGroupEntityWithoutRelations, - createdAt: viewFilterGroupEntity.createdAt.toISOString(), - updatedAt: viewFilterGroupEntity.updatedAt.toISOString(), - deletedAt: viewFilterGroupEntity.deletedAt?.toISOString() ?? null, - universalIdentifier: - viewFilterGroupEntityWithoutRelations.universalIdentifier, + ...viewFilterGroupScalarEntity, + ...relationUniversalIdentifiers, viewFilterIds: viewFilterGroupEntity.viewFilters?.map(({ id }) => id) ?? [], childViewFilterGroupIds: viewFilterGroupEntity.childViewFilterGroups?.map(({ id }) => id) ?? [], - applicationUniversalIdentifier, - parentViewFilterGroupUniversalIdentifier, - viewUniversalIdentifier, viewFilterUniversalIdentifiers: viewFilterGroupEntity.viewFilters?.map( ({ universalIdentifier }) => universalIdentifier, diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-view-filter/utils/from-view-filter-entity-to-flat-view-filter.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-view-filter/utils/from-view-filter-entity-to-flat-view-filter.util.ts index f38ea25790..3c3788313f 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-view-filter/utils/from-view-filter-entity-to-flat-view-filter.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-view-filter/utils/from-view-filter-entity-to-flat-view-filter.util.ts @@ -1,100 +1,26 @@ -import { isDefined, removePropertiesFromRecord } from 'twenty-shared/utils'; - -import { - FlatEntityMapsException, - FlatEntityMapsExceptionCode, -} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception'; -import { getMetadataEntityRelationProperties } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-entity-relation-properties.util'; +import { fromEntityToScalarEntity } from 'src/engine/metadata-modules/flat-entity/utils/from-entity-to-scalar-entity.util'; import { type FlatViewFilter } from 'src/engine/metadata-modules/flat-view-filter/types/flat-view-filter.type'; import { type FromEntityToFlatEntityArgs } from 'src/engine/workspace-cache/types/from-entity-to-flat-entity-args.type'; +import { resolveManyToOneRelationIdsToUniversalIdentifiers } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-many-to-one-relation-ids-to-universal-identifiers.util'; -export const fromViewFilterEntityToFlatViewFilter = ({ - entity: viewFilterEntity, - applicationIdToUniversalIdentifierMap, - fieldMetadataIdToUniversalIdentifierMap, - viewFilterGroupIdToUniversalIdentifierMap, - viewIdToUniversalIdentifierMap, -}: FromEntityToFlatEntityArgs<'viewFilter'>): FlatViewFilter => { - const viewFilterEntityWithoutRelations = removePropertiesFromRecord( - viewFilterEntity, - getMetadataEntityRelationProperties('viewFilter'), - ); +export const fromViewFilterEntityToFlatViewFilter = ( + args: FromEntityToFlatEntityArgs<'viewFilter'>, +): FlatViewFilter => { + const { entity: viewFilterEntity } = args; - const applicationUniversalIdentifier = - applicationIdToUniversalIdentifierMap.get(viewFilterEntity.applicationId); + const viewFilterScalarEntity = fromEntityToScalarEntity({ + metadataName: 'viewFilter', + entity: viewFilterEntity, + }); - if (!isDefined(applicationUniversalIdentifier)) { - throw new FlatEntityMapsException( - `Application with id ${viewFilterEntity.applicationId} not found for viewFilter ${viewFilterEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - - const fieldMetadataUniversalIdentifier = - fieldMetadataIdToUniversalIdentifierMap.get( - viewFilterEntity.fieldMetadataId, - ); - - if (!isDefined(fieldMetadataUniversalIdentifier)) { - throw new FlatEntityMapsException( - `FieldMetadata with id ${viewFilterEntity.fieldMetadataId} not found for viewFilter ${viewFilterEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - - let viewFilterGroupUniversalIdentifier: string | null = null; - - if (isDefined(viewFilterEntity.viewFilterGroupId)) { - viewFilterGroupUniversalIdentifier = - viewFilterGroupIdToUniversalIdentifierMap.get( - viewFilterEntity.viewFilterGroupId, - ) ?? null; - - if (!isDefined(viewFilterGroupUniversalIdentifier)) { - throw new FlatEntityMapsException( - `ViewFilterGroup with id ${viewFilterEntity.viewFilterGroupId} not found for viewFilter ${viewFilterEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - } - - const viewUniversalIdentifier = viewIdToUniversalIdentifierMap.get( - viewFilterEntity.viewId, - ); - - if (!isDefined(viewUniversalIdentifier)) { - throw new FlatEntityMapsException( - `View with id ${viewFilterEntity.viewId} not found for viewFilter ${viewFilterEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - - let relationTargetFieldMetadataUniversalIdentifier: string | null = null; - - if (isDefined(viewFilterEntity.relationTargetFieldMetadataId)) { - relationTargetFieldMetadataUniversalIdentifier = - fieldMetadataIdToUniversalIdentifierMap.get( - viewFilterEntity.relationTargetFieldMetadataId, - ) ?? null; - - if (!isDefined(relationTargetFieldMetadataUniversalIdentifier)) { - throw new FlatEntityMapsException( - `FieldMetadata with id ${viewFilterEntity.relationTargetFieldMetadataId} not found for viewFilter ${viewFilterEntity.id} (relation target)`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - } + const relationUniversalIdentifiers = + resolveManyToOneRelationIdsToUniversalIdentifiers({ + metadataName: 'viewFilter', + ...args, + }); return { - ...viewFilterEntityWithoutRelations, - createdAt: viewFilterEntity.createdAt.toISOString(), - updatedAt: viewFilterEntity.updatedAt.toISOString(), - deletedAt: viewFilterEntity.deletedAt?.toISOString() ?? null, - universalIdentifier: viewFilterEntityWithoutRelations.universalIdentifier, - applicationUniversalIdentifier, - fieldMetadataUniversalIdentifier, - relationTargetFieldMetadataUniversalIdentifier, - viewFilterGroupUniversalIdentifier, - viewUniversalIdentifier, + ...viewFilterScalarEntity, + ...relationUniversalIdentifiers, }; }; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-view-group/utils/from-view-group-entity-to-flat-view-group.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-view-group/utils/from-view-group-entity-to-flat-view-group.util.ts index eb60547411..8216ae1e08 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-view-group/utils/from-view-group-entity-to-flat-view-group.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-view-group/utils/from-view-group-entity-to-flat-view-group.util.ts @@ -1,51 +1,26 @@ -import { isDefined, removePropertiesFromRecord } from 'twenty-shared/utils'; - -import { - FlatEntityMapsException, - FlatEntityMapsExceptionCode, -} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception'; -import { getMetadataEntityRelationProperties } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-entity-relation-properties.util'; +import { fromEntityToScalarEntity } from 'src/engine/metadata-modules/flat-entity/utils/from-entity-to-scalar-entity.util'; import { type FlatViewGroup } from 'src/engine/metadata-modules/flat-view-group/types/flat-view-group.type'; import { type FromEntityToFlatEntityArgs } from 'src/engine/workspace-cache/types/from-entity-to-flat-entity-args.type'; +import { resolveManyToOneRelationIdsToUniversalIdentifiers } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-many-to-one-relation-ids-to-universal-identifiers.util'; -export const fromViewGroupEntityToFlatViewGroup = ({ - entity: viewGroupEntity, - applicationIdToUniversalIdentifierMap, - viewIdToUniversalIdentifierMap, -}: FromEntityToFlatEntityArgs<'viewGroup'>): FlatViewGroup => { - const viewGroupEntityWithoutRelations = removePropertiesFromRecord( - viewGroupEntity, - getMetadataEntityRelationProperties('viewGroup'), - ); +export const fromViewGroupEntityToFlatViewGroup = ( + args: FromEntityToFlatEntityArgs<'viewGroup'>, +): FlatViewGroup => { + const { entity: viewGroupEntity } = args; - const applicationUniversalIdentifier = - applicationIdToUniversalIdentifierMap.get(viewGroupEntity.applicationId); + const viewGroupScalarEntity = fromEntityToScalarEntity({ + metadataName: 'viewGroup', + entity: viewGroupEntity, + }); - if (!isDefined(applicationUniversalIdentifier)) { - throw new FlatEntityMapsException( - `Application with id ${viewGroupEntity.applicationId} not found for viewGroup ${viewGroupEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - - const viewUniversalIdentifier = viewIdToUniversalIdentifierMap.get( - viewGroupEntity.viewId, - ); - - if (!isDefined(viewUniversalIdentifier)) { - throw new FlatEntityMapsException( - `View with id ${viewGroupEntity.viewId} not found for viewGroup ${viewGroupEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } + const relationUniversalIdentifiers = + resolveManyToOneRelationIdsToUniversalIdentifiers({ + metadataName: 'viewGroup', + ...args, + }); return { - ...viewGroupEntityWithoutRelations, - createdAt: viewGroupEntity.createdAt.toISOString(), - updatedAt: viewGroupEntity.updatedAt.toISOString(), - deletedAt: viewGroupEntity.deletedAt?.toISOString() ?? null, - universalIdentifier: viewGroupEntityWithoutRelations.universalIdentifier, - applicationUniversalIdentifier, - viewUniversalIdentifier, + ...viewGroupScalarEntity, + ...relationUniversalIdentifiers, }; }; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-view-sort/utils/from-view-sort-entity-to-flat-view-sort.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-view-sort/utils/from-view-sort-entity-to-flat-view-sort.util.ts index 552e2e288d..f2f8eea770 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-view-sort/utils/from-view-sort-entity-to-flat-view-sort.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-view-sort/utils/from-view-sort-entity-to-flat-view-sort.util.ts @@ -1,63 +1,26 @@ -import { isDefined, removePropertiesFromRecord } from 'twenty-shared/utils'; - -import type { FromEntityToFlatEntityArgs } from 'src/engine/workspace-cache/types/from-entity-to-flat-entity-args.type'; -import { getMetadataEntityRelationProperties } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-entity-relation-properties.util'; -import { - FlatEntityMapsException, - FlatEntityMapsExceptionCode, -} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception'; +import { fromEntityToScalarEntity } from 'src/engine/metadata-modules/flat-entity/utils/from-entity-to-scalar-entity.util'; import { type FlatViewSort } from 'src/engine/metadata-modules/flat-view-sort/types/flat-view-sort.type'; +import { type FromEntityToFlatEntityArgs } from 'src/engine/workspace-cache/types/from-entity-to-flat-entity-args.type'; +import { resolveManyToOneRelationIdsToUniversalIdentifiers } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-many-to-one-relation-ids-to-universal-identifiers.util'; -export const fromViewSortEntityToFlatViewSort = ({ - entity: viewSortEntity, - applicationIdToUniversalIdentifierMap, - viewIdToUniversalIdentifierMap, - fieldMetadataIdToUniversalIdentifierMap, -}: FromEntityToFlatEntityArgs<'viewSort'>): FlatViewSort => { - const viewSortEntityWithoutRelations = removePropertiesFromRecord( - viewSortEntity, - getMetadataEntityRelationProperties('viewSort'), - ); +export const fromViewSortEntityToFlatViewSort = ( + args: FromEntityToFlatEntityArgs<'viewSort'>, +): FlatViewSort => { + const { entity: viewSortEntity } = args; - const applicationUniversalIdentifier = - applicationIdToUniversalIdentifierMap.get(viewSortEntity.applicationId); + const viewSortScalarEntity = fromEntityToScalarEntity({ + metadataName: 'viewSort', + entity: viewSortEntity, + }); - if (!isDefined(applicationUniversalIdentifier)) { - throw new FlatEntityMapsException( - `Application with id ${viewSortEntity.applicationId} not found for viewSort ${viewSortEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - - const viewUniversalIdentifier = viewIdToUniversalIdentifierMap.get( - viewSortEntity.viewId, - ); - - if (!isDefined(viewUniversalIdentifier)) { - throw new FlatEntityMapsException( - `View with id ${viewSortEntity.viewId} not found for viewSort ${viewSortEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - - const fieldMetadataUniversalIdentifier = - fieldMetadataIdToUniversalIdentifierMap.get(viewSortEntity.fieldMetadataId); - - if (!isDefined(fieldMetadataUniversalIdentifier)) { - throw new FlatEntityMapsException( - `Field metadata with id ${viewSortEntity.fieldMetadataId} not found for viewSort ${viewSortEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } + const relationUniversalIdentifiers = + resolveManyToOneRelationIdsToUniversalIdentifiers({ + metadataName: 'viewSort', + ...args, + }); return { - ...viewSortEntityWithoutRelations, - createdAt: viewSortEntity.createdAt.toISOString(), - updatedAt: viewSortEntity.updatedAt.toISOString(), - deletedAt: viewSortEntity.deletedAt?.toISOString() ?? null, - universalIdentifier: viewSortEntityWithoutRelations.universalIdentifier, - applicationUniversalIdentifier, - viewUniversalIdentifier, - fieldMetadataUniversalIdentifier, + ...viewSortScalarEntity, + ...relationUniversalIdentifiers, }; }; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-view/utils/from-view-entity-to-flat-view.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-view/utils/from-view-entity-to-flat-view.util.ts index 0150fcadf5..cf4df67928 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-view/utils/from-view-entity-to-flat-view.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-view/utils/from-view-entity-to-flat-view.util.ts @@ -1,93 +1,26 @@ -import { isDefined, removePropertiesFromRecord } from 'twenty-shared/utils'; +import { isDefined } from 'twenty-shared/utils'; -import { - FlatEntityMapsException, - FlatEntityMapsExceptionCode, -} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception'; -import { getMetadataEntityRelationProperties } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-entity-relation-properties.util'; +import { fromEntityToScalarEntity } from 'src/engine/metadata-modules/flat-entity/utils/from-entity-to-scalar-entity.util'; import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type'; import { fromViewOverridesToUniversalOverrides } from 'src/engine/metadata-modules/flat-view/utils/from-view-overrides-to-universal-overrides.util'; import { type FromEntityToFlatEntityArgs } from 'src/engine/workspace-cache/types/from-entity-to-flat-entity-args.type'; +import { resolveManyToOneRelationIdsToUniversalIdentifiers } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-many-to-one-relation-ids-to-universal-identifiers.util'; -export const fromViewEntityToFlatView = ({ - entity: viewEntity, - applicationIdToUniversalIdentifierMap, - objectMetadataIdToUniversalIdentifierMap, - fieldMetadataIdToUniversalIdentifierMap, -}: FromEntityToFlatEntityArgs<'view'>): FlatView => { - const viewEntityWithoutRelations = removePropertiesFromRecord( - viewEntity, - getMetadataEntityRelationProperties('view'), - ); +export const fromViewEntityToFlatView = ( + args: FromEntityToFlatEntityArgs<'view'>, +): FlatView => { + const { entity: viewEntity, fieldMetadataIdToUniversalIdentifierMap } = args; - const applicationUniversalIdentifier = - applicationIdToUniversalIdentifierMap.get(viewEntity.applicationId); + const viewScalarEntity = fromEntityToScalarEntity({ + metadataName: 'view', + entity: viewEntity, + }); - if (!isDefined(applicationUniversalIdentifier)) { - throw new FlatEntityMapsException( - `Application with id ${viewEntity.applicationId} not found for view ${viewEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - - const objectMetadataUniversalIdentifier = - objectMetadataIdToUniversalIdentifierMap.get(viewEntity.objectMetadataId); - - if (!isDefined(objectMetadataUniversalIdentifier)) { - throw new FlatEntityMapsException( - `ObjectMetadata with id ${viewEntity.objectMetadataId} not found for view ${viewEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - - let kanbanAggregateOperationFieldMetadataUniversalIdentifier: string | null = - null; - - if (isDefined(viewEntity.kanbanAggregateOperationFieldMetadataId)) { - kanbanAggregateOperationFieldMetadataUniversalIdentifier = - fieldMetadataIdToUniversalIdentifierMap.get( - viewEntity.kanbanAggregateOperationFieldMetadataId, - ) ?? null; - - if (!isDefined(kanbanAggregateOperationFieldMetadataUniversalIdentifier)) { - throw new FlatEntityMapsException( - `FieldMetadata with id ${viewEntity.kanbanAggregateOperationFieldMetadataId} not found for view ${viewEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - } - - let calendarFieldMetadataUniversalIdentifier: string | null = null; - - if (isDefined(viewEntity.calendarFieldMetadataId)) { - calendarFieldMetadataUniversalIdentifier = - fieldMetadataIdToUniversalIdentifierMap.get( - viewEntity.calendarFieldMetadataId, - ) ?? null; - - if (!isDefined(calendarFieldMetadataUniversalIdentifier)) { - throw new FlatEntityMapsException( - `FieldMetadata with id ${viewEntity.calendarFieldMetadataId} not found for view ${viewEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - } - - let mainGroupByFieldMetadataUniversalIdentifier: string | null = null; - - if (isDefined(viewEntity.mainGroupByFieldMetadataId)) { - mainGroupByFieldMetadataUniversalIdentifier = - fieldMetadataIdToUniversalIdentifierMap.get( - viewEntity.mainGroupByFieldMetadataId, - ) ?? null; - - if (!isDefined(mainGroupByFieldMetadataUniversalIdentifier)) { - throw new FlatEntityMapsException( - `FieldMetadata with id ${viewEntity.mainGroupByFieldMetadataId} not found for view ${viewEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } - } + const relationUniversalIdentifiers = + resolveManyToOneRelationIdsToUniversalIdentifiers({ + metadataName: 'view', + ...args, + }); const universalOverrides = isDefined(viewEntity.overrides) ? fromViewOverridesToUniversalOverrides({ @@ -100,22 +33,14 @@ export const fromViewEntityToFlatView = ({ : null; return { - ...viewEntityWithoutRelations, - createdAt: viewEntity.createdAt.toISOString(), - updatedAt: viewEntity.updatedAt.toISOString(), - deletedAt: viewEntity.deletedAt?.toISOString() ?? null, - universalIdentifier: viewEntityWithoutRelations.universalIdentifier, + ...viewScalarEntity, + ...relationUniversalIdentifiers, universalOverrides, viewFieldIds: viewEntity.viewFields.map(({ id }) => id), viewFieldGroupIds: viewEntity.viewFieldGroups?.map(({ id }) => id) ?? [], viewFilterIds: viewEntity.viewFilters.map(({ id }) => id), viewGroupIds: viewEntity.viewGroups.map(({ id }) => id), viewFilterGroupIds: viewEntity.viewFilterGroups?.map(({ id }) => id) ?? [], - applicationUniversalIdentifier, - objectMetadataUniversalIdentifier, - kanbanAggregateOperationFieldMetadataUniversalIdentifier, - calendarFieldMetadataUniversalIdentifier, - mainGroupByFieldMetadataUniversalIdentifier, viewFieldUniversalIdentifiers: viewEntity.viewFields.map( ({ universalIdentifier }) => universalIdentifier, ), diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-webhook/utils/from-webhook-entity-to-flat-webhook.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-webhook/utils/from-webhook-entity-to-flat-webhook.util.ts index 15285c7630..363190a811 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-webhook/utils/from-webhook-entity-to-flat-webhook.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-webhook/utils/from-webhook-entity-to-flat-webhook.util.ts @@ -1,38 +1,26 @@ -import { isDefined } from 'twenty-shared/utils'; - -import { - FlatEntityMapsException, - FlatEntityMapsExceptionCode, -} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception'; +import { fromEntityToScalarEntity } from 'src/engine/metadata-modules/flat-entity/utils/from-entity-to-scalar-entity.util'; import { type FlatWebhook } from 'src/engine/metadata-modules/flat-webhook/types/flat-webhook.type'; import { type FromEntityToFlatEntityArgs } from 'src/engine/workspace-cache/types/from-entity-to-flat-entity-args.type'; +import { resolveManyToOneRelationIdsToUniversalIdentifiers } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-many-to-one-relation-ids-to-universal-identifiers.util'; -export const fromWebhookEntityToFlatWebhook = ({ - entity: webhookEntity, - applicationIdToUniversalIdentifierMap, -}: FromEntityToFlatEntityArgs<'webhook'>): FlatWebhook => { - const applicationUniversalIdentifier = - applicationIdToUniversalIdentifierMap.get(webhookEntity.applicationId); +export const fromWebhookEntityToFlatWebhook = ( + args: FromEntityToFlatEntityArgs<'webhook'>, +): FlatWebhook => { + const { entity: webhookEntity } = args; - if (!isDefined(applicationUniversalIdentifier)) { - throw new FlatEntityMapsException( - `Application with id ${webhookEntity.applicationId} not found for webhook ${webhookEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } + const webhookScalarEntity = fromEntityToScalarEntity({ + metadataName: 'webhook', + entity: webhookEntity, + }); + + const relationUniversalIdentifiers = + resolveManyToOneRelationIdsToUniversalIdentifiers({ + metadataName: 'webhook', + ...args, + }); return { - id: webhookEntity.id, - targetUrl: webhookEntity.targetUrl, - operations: webhookEntity.operations, - description: webhookEntity.description, - secret: webhookEntity.secret, - workspaceId: webhookEntity.workspaceId, - universalIdentifier: webhookEntity.universalIdentifier, - applicationId: webhookEntity.applicationId, - createdAt: webhookEntity.createdAt.toISOString(), - updatedAt: webhookEntity.updatedAt.toISOString(), - deletedAt: webhookEntity.deletedAt?.toISOString() ?? null, - applicationUniversalIdentifier, + ...webhookScalarEntity, + ...relationUniversalIdentifiers, }; }; diff --git a/packages/twenty-server/src/engine/metadata-modules/logic-function/utils/from-logic-function-entity-to-flat-logic-function.util.ts b/packages/twenty-server/src/engine/metadata-modules/logic-function/utils/from-logic-function-entity-to-flat-logic-function.util.ts index 4198b1e69e..9563bf0315 100644 --- a/packages/twenty-server/src/engine/metadata-modules/logic-function/utils/from-logic-function-entity-to-flat-logic-function.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/logic-function/utils/from-logic-function-entity-to-flat-logic-function.util.ts @@ -1,39 +1,26 @@ -import { isDefined, removePropertiesFromRecord } from 'twenty-shared/utils'; - -import { - FlatEntityMapsException, - FlatEntityMapsExceptionCode, -} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception'; -import { getMetadataEntityRelationProperties } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-entity-relation-properties.util'; +import { fromEntityToScalarEntity } from 'src/engine/metadata-modules/flat-entity/utils/from-entity-to-scalar-entity.util'; import { type FlatLogicFunction } from 'src/engine/metadata-modules/logic-function/types/flat-logic-function.type'; import { type FromEntityToFlatEntityArgs } from 'src/engine/workspace-cache/types/from-entity-to-flat-entity-args.type'; +import { resolveManyToOneRelationIdsToUniversalIdentifiers } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-many-to-one-relation-ids-to-universal-identifiers.util'; -export const fromLogicFunctionEntityToFlatLogicFunction = ({ - entity: logicFunctionEntity, - applicationIdToUniversalIdentifierMap, -}: FromEntityToFlatEntityArgs<'logicFunction'>): FlatLogicFunction => { - const applicationUniversalIdentifier = - applicationIdToUniversalIdentifierMap.get( - logicFunctionEntity.applicationId, - ); +export const fromLogicFunctionEntityToFlatLogicFunction = ( + args: FromEntityToFlatEntityArgs<'logicFunction'>, +): FlatLogicFunction => { + const { entity: logicFunctionEntity } = args; - if (!isDefined(applicationUniversalIdentifier)) { - throw new FlatEntityMapsException( - `Application with id ${logicFunctionEntity.applicationId} not found for logicFunction ${logicFunctionEntity.id}`, - FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, - ); - } + const logicFunctionScalarEntity = fromEntityToScalarEntity({ + metadataName: 'logicFunction', + entity: logicFunctionEntity, + }); - const logicFunctionWithoutRelations = removePropertiesFromRecord( - logicFunctionEntity, - getMetadataEntityRelationProperties('logicFunction'), - ); + const relationUniversalIdentifiers = + resolveManyToOneRelationIdsToUniversalIdentifiers({ + metadataName: 'logicFunction', + ...args, + }); return { - ...logicFunctionWithoutRelations, - createdAt: logicFunctionEntity.createdAt.toISOString(), - updatedAt: logicFunctionEntity.updatedAt.toISOString(), - deletedAt: logicFunctionEntity.deletedAt?.toISOString() ?? null, - applicationUniversalIdentifier, + ...logicFunctionScalarEntity, + ...relationUniversalIdentifiers, }; }; diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/__tests__/resolve-many-to-one-relation-ids-to-universal-identifiers.util.spec.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/__tests__/resolve-many-to-one-relation-ids-to-universal-identifiers.util.spec.ts new file mode 100644 index 0000000000..711eb7d997 --- /dev/null +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/__tests__/resolve-many-to-one-relation-ids-to-universal-identifiers.util.spec.ts @@ -0,0 +1,236 @@ +import { type AllMetadataName } from 'twenty-shared/metadata'; + +import { FlatEntityMapsExceptionCode } from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception'; +import { type EntityManyToOneIdByUniversalIdentifierMaps } from 'src/engine/workspace-cache/types/entity-many-to-one-id-by-universal-identifier-maps.type'; +import { type FromEntityToFlatEntityArgs } from 'src/engine/workspace-cache/types/from-entity-to-flat-entity-args.type'; +import { resolveManyToOneRelationIdsToUniversalIdentifiers } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-many-to-one-relation-ids-to-universal-identifiers.util'; + +const buildArgs = ( + args: { + metadataName: T; + entity: Record; + } & EntityManyToOneIdByUniversalIdentifierMaps, +): { metadataName: T } & FromEntityToFlatEntityArgs => ({ + ...args, + entity: args.entity as unknown as FromEntityToFlatEntityArgs['entity'], +}); + +describe('resolveManyToOneRelationIdsToUniversalIdentifiers', () => { + describe('application resolution', () => { + it('should resolve only the application universal identifier when the entity has no other many-to-one relations', () => { + const result = resolveManyToOneRelationIdsToUniversalIdentifiers( + buildArgs({ + metadataName: 'permissionFlag', + entity: { + id: 'permission-flag-id-1', + applicationId: 'app-id-1', + }, + applicationIdToUniversalIdentifierMap: new Map([ + ['app-id-1', 'app-ui-1'], + ]), + }), + ); + + expect(result).toEqual({ + applicationUniversalIdentifier: 'app-ui-1', + }); + }); + + it('should throw when the applicationId is not present in the map', () => { + expect(() => + resolveManyToOneRelationIdsToUniversalIdentifiers( + buildArgs({ + metadataName: 'permissionFlag', + entity: { + id: 'permission-flag-id-1', + applicationId: 'app-id-1', + }, + applicationIdToUniversalIdentifierMap: new Map(), + }), + ), + ).toThrow( + expect.objectContaining({ + code: FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, + }), + ); + }); + + it('should throw when the entity has no applicationId', () => { + expect(() => + resolveManyToOneRelationIdsToUniversalIdentifiers( + buildArgs({ + metadataName: 'permissionFlag', + entity: { + id: 'permission-flag-id-1', + applicationId: null, + }, + applicationIdToUniversalIdentifierMap: new Map([ + ['app-id-1', 'app-ui-1'], + ]), + }), + ), + ).toThrow( + expect.objectContaining({ + code: FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, + }), + ); + }); + }); + + describe('non-nullable relations', () => { + it('should resolve universal identifiers for application and non-nullable foreign keys', () => { + const result = resolveManyToOneRelationIdsToUniversalIdentifiers( + buildArgs({ + metadataName: 'viewField', + entity: { + id: 'view-field-id-1', + applicationId: 'app-id-1', + fieldMetadataId: 'field-id-1', + viewId: 'view-id-1', + viewFieldGroupId: 'view-field-group-id-1', + }, + applicationIdToUniversalIdentifierMap: new Map([ + ['app-id-1', 'app-ui-1'], + ]), + fieldMetadataIdToUniversalIdentifierMap: new Map([ + ['field-id-1', 'field-ui-1'], + ]), + viewIdToUniversalIdentifierMap: new Map([['view-id-1', 'view-ui-1']]), + viewFieldGroupIdToUniversalIdentifierMap: new Map([ + ['view-field-group-id-1', 'view-field-group-ui-1'], + ]), + }), + ); + + expect(result).toEqual({ + applicationUniversalIdentifier: 'app-ui-1', + fieldMetadataUniversalIdentifier: 'field-ui-1', + viewUniversalIdentifier: 'view-ui-1', + viewFieldGroupUniversalIdentifier: 'view-field-group-ui-1', + }); + }); + + it('should throw when a non-nullable foreign key id is missing from the map', () => { + expect(() => + resolveManyToOneRelationIdsToUniversalIdentifiers( + buildArgs({ + metadataName: 'viewField', + entity: { + id: 'view-field-id-1', + applicationId: 'app-id-1', + fieldMetadataId: 'non-existent-field-id', + viewId: 'view-id-1', + viewFieldGroupId: null, + }, + applicationIdToUniversalIdentifierMap: new Map([ + ['app-id-1', 'app-ui-1'], + ]), + fieldMetadataIdToUniversalIdentifierMap: new Map(), + viewIdToUniversalIdentifierMap: new Map([ + ['view-id-1', 'view-ui-1'], + ]), + viewFieldGroupIdToUniversalIdentifierMap: new Map(), + }), + ), + ).toThrow( + expect.objectContaining({ + code: FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, + }), + ); + }); + + it('should throw a malformed-entity error when a non-nullable foreign key is itself missing', () => { + expect(() => + resolveManyToOneRelationIdsToUniversalIdentifiers( + buildArgs({ + metadataName: 'viewField', + entity: { + id: 'view-field-id-1', + applicationId: 'app-id-1', + fieldMetadataId: null, + viewId: 'view-id-1', + viewFieldGroupId: null, + }, + applicationIdToUniversalIdentifierMap: new Map([ + ['app-id-1', 'app-ui-1'], + ]), + fieldMetadataIdToUniversalIdentifierMap: new Map([ + ['field-id-1', 'field-ui-1'], + ]), + viewIdToUniversalIdentifierMap: new Map([ + ['view-id-1', 'view-ui-1'], + ]), + viewFieldGroupIdToUniversalIdentifierMap: new Map(), + }), + ), + ).toThrow( + expect.objectContaining({ + code: FlatEntityMapsExceptionCode.ENTITY_MALFORMED, + }), + ); + }); + }); + + describe('nullable relations', () => { + it('should resolve a nullable foreign key to null when its id is not defined', () => { + const result = resolveManyToOneRelationIdsToUniversalIdentifiers( + buildArgs({ + metadataName: 'viewField', + entity: { + id: 'view-field-id-1', + applicationId: 'app-id-1', + fieldMetadataId: 'field-id-1', + viewId: 'view-id-1', + viewFieldGroupId: null, + }, + applicationIdToUniversalIdentifierMap: new Map([ + ['app-id-1', 'app-ui-1'], + ]), + fieldMetadataIdToUniversalIdentifierMap: new Map([ + ['field-id-1', 'field-ui-1'], + ]), + viewIdToUniversalIdentifierMap: new Map([['view-id-1', 'view-ui-1']]), + viewFieldGroupIdToUniversalIdentifierMap: new Map(), + }), + ); + + expect(result).toEqual({ + applicationUniversalIdentifier: 'app-ui-1', + fieldMetadataUniversalIdentifier: 'field-ui-1', + viewUniversalIdentifier: 'view-ui-1', + viewFieldGroupUniversalIdentifier: null, + }); + }); + + it('should throw when a nullable foreign key has an id that is missing from the map', () => { + expect(() => + resolveManyToOneRelationIdsToUniversalIdentifiers( + buildArgs({ + metadataName: 'viewField', + entity: { + id: 'view-field-id-1', + applicationId: 'app-id-1', + fieldMetadataId: 'field-id-1', + viewId: 'view-id-1', + viewFieldGroupId: 'non-existent-view-field-group-id', + }, + applicationIdToUniversalIdentifierMap: new Map([ + ['app-id-1', 'app-ui-1'], + ]), + fieldMetadataIdToUniversalIdentifierMap: new Map([ + ['field-id-1', 'field-ui-1'], + ]), + viewIdToUniversalIdentifierMap: new Map([ + ['view-id-1', 'view-ui-1'], + ]), + viewFieldGroupIdToUniversalIdentifierMap: new Map(), + }), + ), + ).toThrow( + expect.objectContaining({ + code: FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, + }), + ); + }); + }); +}); diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-many-to-one-relation-ids-to-universal-identifiers.util.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-many-to-one-relation-ids-to-universal-identifiers.util.ts new file mode 100644 index 0000000000..0899fde1f9 --- /dev/null +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-many-to-one-relation-ids-to-universal-identifiers.util.ts @@ -0,0 +1,130 @@ +import { type AllMetadataName } from 'twenty-shared/metadata'; +import { capitalize, isDefined } from 'twenty-shared/utils'; + +import { ALL_MANY_TO_ONE_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-many-to-one-metadata-relations.constant'; +import { + FlatEntityMapsException, + FlatEntityMapsExceptionCode, +} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception'; +import { type FromEntityToFlatEntityArgs } from 'src/engine/workspace-cache/types/from-entity-to-flat-entity-args.type'; + +type ManyToOneRelationsConfig = + (typeof ALL_MANY_TO_ONE_METADATA_RELATIONS)[T]; + +// Mirrors ResolvedForeignKeyIds from resolveUniversalRelationIdentifiersToIds but +// keyed on universalForeignKey, so the resolved record is the many-to-one part of a +// flat entity (plus the always-present applicationUniversalIdentifier). +export type ResolvedManyToOneRelationUniversalIdentifiers< + T extends AllMetadataName, +> = { + [K in keyof ManyToOneRelationsConfig as ManyToOneRelationsConfig[K] extends { + universalForeignKey: infer UniversalForeignKey extends string; + } + ? UniversalForeignKey + : never]: ManyToOneRelationsConfig[K] extends { isNullable: true } + ? string | null + : string; +} & { + applicationUniversalIdentifier: string; +}; + +export const resolveManyToOneRelationIdsToUniversalIdentifiers = < + T extends AllMetadataName, +>({ + metadataName, + entity, + ...idToUniversalIdentifierMaps +}: { + metadataName: T; +} & FromEntityToFlatEntityArgs): ResolvedManyToOneRelationUniversalIdentifiers => { + const readEntityForeignKey = ( + propertyName: string, + ): string | null | undefined => + entity[propertyName as keyof typeof entity] as string | null | undefined; + + const entityId = readEntityForeignKey('id'); + const resolvedUniversalIdentifierByForeignKey: Record = + {}; + + const applicationId = readEntityForeignKey('applicationId'); + const applicationUniversalIdentifier = isDefined(applicationId) + ? idToUniversalIdentifierMaps.applicationIdToUniversalIdentifierMap.get( + applicationId, + ) + : undefined; + + if (!isDefined(applicationUniversalIdentifier)) { + throw new FlatEntityMapsException( + `Application with id ${applicationId} not found for ${metadataName} ${entityId}`, + FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, + ); + } + + resolvedUniversalIdentifierByForeignKey.applicationUniversalIdentifier = + applicationUniversalIdentifier; + + const relationEntries = ALL_MANY_TO_ONE_METADATA_RELATIONS[metadataName]; + + for (const relationPropertyName of Object.keys(relationEntries)) { + const relationEntry = relationEntries[ + relationPropertyName as keyof typeof relationEntries + ] as { + foreignKey: string; + metadataName: AllMetadataName; + isNullable: boolean; + universalForeignKey: string; + } | null; + + if (!isDefined(relationEntry)) { + continue; + } + + const { + foreignKey, + metadataName: targetMetadataName, + universalForeignKey, + isNullable, + } = relationEntry; + const foreignKeyId = readEntityForeignKey(foreignKey); + + if (!isDefined(foreignKeyId)) { + if (isNullable) { + resolvedUniversalIdentifierByForeignKey[universalForeignKey] = null; + + continue; + } + + throw new FlatEntityMapsException( + `Missing non-nullable foreign key ${foreignKey} on ${metadataName} ${entityId} (relation to ${targetMetadataName})`, + FlatEntityMapsExceptionCode.ENTITY_MALFORMED, + ); + } + + const mapKey = `${targetMetadataName}IdToUniversalIdentifierMap`; + const targetIdToUniversalIdentifierMap = idToUniversalIdentifierMaps[ + mapKey as keyof typeof idToUniversalIdentifierMaps + ] as unknown as Map | undefined; + + if (!isDefined(targetIdToUniversalIdentifierMap)) { + throw new FlatEntityMapsException( + `Missing ${mapKey} when resolving ${metadataName} ${entityId}`, + FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, + ); + } + + const universalIdentifier = + targetIdToUniversalIdentifierMap.get(foreignKeyId); + + if (!isDefined(universalIdentifier)) { + throw new FlatEntityMapsException( + `${capitalize(targetMetadataName)} with id ${foreignKeyId} not found for ${metadataName} ${entityId}`, + FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND, + ); + } + + resolvedUniversalIdentifierByForeignKey[universalForeignKey] = + universalIdentifier; + } + + return resolvedUniversalIdentifierByForeignKey as ResolvedManyToOneRelationUniversalIdentifiers; +}; diff --git a/packages/twenty-server/test/integration/metadata/suites/application/__snapshots__/successful-sync-application-workspace-migration.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/application/__snapshots__/successful-sync-application-workspace-migration.integration-spec.ts.snap index 024c0f1a44..1e02effefa 100644 --- a/packages/twenty-server/test/integration/metadata/suites/application/__snapshots__/successful-sync-application-workspace-migration.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/metadata/suites/application/__snapshots__/successful-sync-application-workspace-migration.integration-spec.ts.snap @@ -400,7 +400,6 @@ exports[`syncApplication should delete old field and create equivalent one when "isSystem": false, "isSystemSideEffect": false, "isUIEditable": true, - "isUIReadOnly": false, "isUnique": false, "kanbanAggregateOperationViewIds": [], "kanbanAggregateOperationViewUniversalIdentifiers": [],