Solo transaction application synchronization service refactor (#17864)

# Introduction
Refactoring the application sync service to be making a single validate
build and run transaction instead of calling all the services n times
thanks to the universal workspace migration refactor that allow doing so

## What's next
Migrating all below entities to by syncableEntities so they can be
universalised too ( right now they're still calling the services n times
and won't be returned in the workspace migration )
-
[objectPermission](https://github.com/twentyhq/core-team-issues/issues/2223)
-
[fieldPermission](https://github.com/twentyhq/core-team-issues/issues/2224)
-
[permissionFlag](https://github.com/twentyhq/core-team-issues/issues/2225)
This commit is contained in:
Paul Rastoin
2026-02-11 17:30:01 +01:00
committed by GitHub
parent 0ee63a0525
commit b2f7c745f8
27 changed files with 848 additions and 1127 deletions
@@ -17,7 +17,7 @@ import { TWENTY_STANDARD_ALL_METADATA_NAME } from 'src/engine/workspace-manager/
import { computeTwentyStandardApplicationAllFlatEntityMaps } from 'src/engine/workspace-manager/twenty-standard-application/utils/twenty-standard-application-all-flat-entity-maps.constant';
import { WorkspaceMigrationBuilderException } from 'src/engine/workspace-manager/workspace-migration/exceptions/workspace-migration-builder-exception';
import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration/services/workspace-migration-validate-build-and-run-service';
import { FromToAllFlatEntityMaps } from 'src/engine/workspace-manager/workspace-migration/types/workspace-migration-orchestrator.type';
import { FromToAllUniversalFlatEntityMaps } from 'src/engine/workspace-manager/workspace-migration/types/workspace-migration-orchestrator.type';
import { FavoriteWorkspaceEntity } from 'src/modules/favorite/standard-objects/favorite.workspace-entity';
@Injectable()
@@ -87,7 +87,7 @@ export class TwentyStandardApplicationService {
shouldIncludeRecordPageLayouts,
});
const fromToAllFlatEntityMaps: FromToAllFlatEntityMaps = {};
const fromToAllFlatEntityMaps: FromToAllUniversalFlatEntityMaps = {};
for (const metadataName of TWENTY_STANDARD_ALL_METADATA_NAME) {
const flatEntityMapsKey = getMetadataFlatEntityMapsKey(metadataName);
@@ -23,7 +23,7 @@ import {
import { WorkspaceMigrationBuildOrchestratorService } from 'src/engine/workspace-manager/workspace-migration/services/workspace-migration-build-orchestrator.service';
import { WorkspaceMigrationBuilderAdditionalCacheDataMaps } from 'src/engine/workspace-manager/workspace-migration/types/workspace-migration-builder-additional-cache-data-maps.type';
import {
FromToAllFlatEntityMaps,
FromToAllUniversalFlatEntityMaps,
WorkspaceMigrationOrchestratorBuildArgs,
WorkspaceMigrationOrchestratorFailedResult,
} from 'src/engine/workspace-manager/workspace-migration/types/workspace-migration-orchestrator.type';
@@ -133,7 +133,7 @@ export class WorkspaceMigrationValidateBuildAndRunService {
workspaceId,
applicationUniversalIdentifier,
}: ValidateBuildAndRunWorkspaceMigrationFromMatriceArgs): Promise<{
fromToAllFlatEntityMaps: FromToAllFlatEntityMaps;
fromToAllFlatEntityMaps: FromToAllUniversalFlatEntityMaps;
inferDeletionFromMissingEntities: InferDeletionFromMissingEntities;
dependencyAllFlatEntityMaps: Partial<AllFlatEntityMaps>;
additionalCacheDataMaps: WorkspaceMigrationBuilderAdditionalCacheDataMaps;
@@ -149,7 +149,7 @@ export class WorkspaceMigrationValidateBuildAndRunService {
applicationUniversalIdentifier,
});
const fromToAllFlatEntityMaps: FromToAllFlatEntityMaps = {};
const fromToAllFlatEntityMaps: FromToAllUniversalFlatEntityMaps = {};
const inferDeletionFromMissingEntities: InferDeletionFromMissingEntities =
{};
const idByUniversalIdentifierByMetadataName: IdByUniversalIdentifierByMetadataName =
@@ -11,7 +11,7 @@ import { type FailedFlatEntityValidation } from 'src/engine/workspace-manager/wo
import { type WorkspaceMigration } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/workspace-migration';
import { type WorkspaceMigrationBuilderOptions } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/workspace-migration-builder-options.type';
export type FromToAllFlatEntityMaps = {
export type FromToAllUniversalFlatEntityMaps = {
[P in keyof AllUniversalFlatEntityMaps]?: FromTo<
AllUniversalFlatEntityMaps[P]
>;
@@ -20,7 +20,7 @@ export type FromToAllFlatEntityMaps = {
export type WorkspaceMigrationOrchestratorBuildArgs = {
workspaceId: string;
buildOptions: WorkspaceMigrationBuilderOptions;
fromToAllFlatEntityMaps: FromToAllFlatEntityMaps;
fromToAllFlatEntityMaps: FromToAllUniversalFlatEntityMaps;
dependencyAllFlatEntityMaps?: Partial<AllUniversalFlatEntityMaps>;
additionalCacheDataMaps: WorkspaceMigrationBuilderAdditionalCacheDataMaps;
applicationUniversalIdentifier: string;
@@ -1,7 +1,9 @@
import { type Equal, type Expect } from 'twenty-shared/testing';
import { type SerializedRelation } from 'twenty-shared/types';
import {
type SerializedRelation,
type FormatRecordSerializedRelationProperties,
} from 'twenty-shared/types';
import { type FormatRecordSerializedRelationProperties } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/format-record-serialized-relation-properties.type';
import { type JsonbProperty } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/jsonb-property.type';
type ObjectWithRelation = {
@@ -1,41 +0,0 @@
import {
type ExtractSerializedRelationProperties,
type IsSerializedRelation,
} from 'twenty-shared/types';
import { type RemoveSuffix } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/remove-suffix.type';
// Determines if a property should be transformed to a universal identifier
// A property is transformed only if:
// 1. It matches ExtractSerializedRelationProperties (has the serialized relation brand)
// 2. Its value does NOT have a string index signature (not a Record<string, X>)
type ShouldTransformToUniversalIdentifier<T, P extends keyof T> = [P] extends [
ExtractSerializedRelationProperties<T>,
]
? string extends keyof NonNullable<T[P]>
? false
: true
: false;
export type FormatRecordSerializedRelationProperties<T> = T extends unknown
? T extends (infer U)[]
? FormatRecordSerializedRelationProperties<U>[]
: T extends string
? // By definition we assume that any SerializedRelation are not enforced at pg scope through an FK
// Which mean that SerializedRelation might resolve to non-existent entities ( null )
IsSerializedRelation<T> extends true
? T | null
: T
: T extends object
? {
[P in keyof T as ShouldTransformToUniversalIdentifier<
T,
P
> extends true
? P extends string
? `${RemoveSuffix<P, 'Id'>}UniversalIdentifier`
: P
: P]: FormatRecordSerializedRelationProperties<T[P]>;
}
: T
: never;
@@ -1,4 +1,5 @@
import { type AllMetadataName } from 'twenty-shared/metadata';
import { type FormatRecordSerializedRelationProperties } from 'twenty-shared/types';
import { type ALL_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-metadata-relations.constant';
import { type AddSuffixToEntityManyToOneProperties } from 'src/engine/metadata-modules/flat-entity/types/add-suffix-to-entity-many-to-one-properties.type';
@@ -8,7 +9,6 @@ import { type FromMetadataEntityToMetadataName } from 'src/engine/metadata-modul
import { type MetadataManyToOneJoinColumn } from 'src/engine/metadata-modules/flat-entity/types/metadata-many-to-one-join-column.type';
import { type SyncableEntity } from 'src/engine/workspace-manager/types/syncable-entity.interface';
import { type AllJsonbPropertiesWithSerializedPropertiesForMetadataName } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/constants/all-jsonb-properties-with-serialized-relation-by-metadata-name.constant';
import { type FormatRecordSerializedRelationProperties } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/format-record-serialized-relation-properties.type';
export type UniversalSyncableFlatEntity = Omit<
SyncableEntity,
@@ -1,4 +0,0 @@
export type RemoveSuffix<
T extends string,
P extends string,
> = T extends `${infer Prefix}${P}` ? Prefix : T;