Extract field metadata build out of object metadata build (#14763)
# Introduction Extracting the legacy fields build and dispatch out of the object one to follow the generic flat entity build, also update caches entries for object ## Main tasks - flat field map cache - flat field builder - refactored the dispatch matrix to return flat entity maps instead of flat entity arrays - orchestrator aggregator - removing legacy code - making universal identifier aka standardId of standard field for custom object deterministically dynamic - perfs debug logs for v2 ## TODO - [x] Refactor the generic entity builder to be dependency flat maps in order to main foreign keys list in flat parent - [x] Refactor the flat object metadata to contain the array of related fields and avoid costy find object fields - [ ] Improve the create field handler to handle multiple field at once - [ ] Refactor the dispatch to embbed the comparison - [ ] Improve perf by extracting from elements out of existing - [ ] Fix the labelIdentifierId validators on object before field creation ( integ tests are in failing mode ) ## Debug logs snippet ```ts [EntityBuilder fieldMetadata] matrix computation: 0.027ms [EntityBuilder fieldMetadata] creation validation: 0.001ms [EntityBuilder fieldMetadata] deletion validation: 0.293ms [EntityBuilder fieldMetadata] update validation: 0.006ms [EntityBuilder fieldMetadata] entity processing: 0.363ms [EntityBuilder fieldMetadata] validateAndBuild: 0.455ms [EntityBuilder index] matrix computation: 0.005ms [EntityBuilder index] creation validation: 0.001ms [EntityBuilder index] deletion validation: 0.146ms [EntityBuilder index] update validation: 0.004ms [EntityBuilder index] entity processing: 0.199ms [EntityBuilder index] validateAndBuild: 0.228ms [Runner] Initial cache retrieval: 0.549ms [BaseWorkspaceMigrationRunnerActionHandlerService] delete_index executeForWorkspaceSchema: 11.665ms [BaseWorkspaceMigrationRunnerActionHandlerService] delete_index executeForMetadata: 12.864ms [BaseWorkspaceMigrationRunnerActionHandlerService] delete_field executeForWorkspaceSchema: 1.476ms [BaseWorkspaceMigrationRunnerActionHandlerService] delete_field executeForMetadata: 6.816ms [BaseWorkspaceMigrationRunnerActionHandlerService] delete_field executeForWorkspaceSchema: 0.062ms [BaseWorkspaceMigrationRunnerActionHandlerService] delete_field executeForMetadata: 0.889ms [Runner] Transaction execution: 23.434ms [Runner] Cache invalidation: 316.662ms [Runner] Total execution: 340.767ms ``` As you can see cache invalidation is way to long, we could replace the cache by the optimistic in the end
This commit is contained in:
+1
-1
@@ -111,7 +111,7 @@ export const PET_DATA_SEEDS: PetDataSeed[] = [
|
||||
interestingFacts: [
|
||||
'World’s Best Sock Thief',
|
||||
'Expert at Puppy Eyes',
|
||||
'Fearless… Except Around Bananas',
|
||||
'Fearless Except Around Bananas',
|
||||
'Signature Ear Flip',
|
||||
],
|
||||
extraData: JSON.stringify({
|
||||
|
||||
+1
@@ -243,6 +243,7 @@ export class DevSeederDataService {
|
||||
);
|
||||
|
||||
if (!objectMetadata) {
|
||||
// TODO this continue is hacky, we should have a record seed config per workspace
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
+9
-2
@@ -1,5 +1,6 @@
|
||||
import { FieldMetadataType, RelationType } from 'twenty-shared/types';
|
||||
|
||||
import { ROCKET_CUSTOM_OBJECT_SEED } from 'src/engine/workspace-manager/dev-seeder/metadata/custom-objects/constants/rocket-custom-object-seed.constant';
|
||||
import { type FieldMetadataSeed } from 'src/engine/workspace-manager/dev-seeder/metadata/types/field-metadata-seed.type';
|
||||
|
||||
export const PET_CUSTOM_RELATION_FIELD_SEEDS: (FieldMetadataSeed & {
|
||||
@@ -18,7 +19,10 @@ export const PET_CUSTOM_RELATION_FIELD_SEEDS: (FieldMetadataSeed & {
|
||||
targetFieldIcon: 'IconRelationOneToMany',
|
||||
},
|
||||
],
|
||||
targetObjectMetadataNames: ['surveyResult', 'rocket'],
|
||||
targetObjectMetadataNames: [
|
||||
'surveyResult',
|
||||
ROCKET_CUSTOM_OBJECT_SEED.nameSingular,
|
||||
],
|
||||
},
|
||||
{
|
||||
type: FieldMetadataType.MORPH_RELATION,
|
||||
@@ -33,6 +37,9 @@ export const PET_CUSTOM_RELATION_FIELD_SEEDS: (FieldMetadataSeed & {
|
||||
targetFieldIcon: 'IconRelationOneToMany',
|
||||
},
|
||||
],
|
||||
targetObjectMetadataNames: ['surveyResult', 'rocket'],
|
||||
targetObjectMetadataNames: [
|
||||
'surveyResult',
|
||||
ROCKET_CUSTOM_OBJECT_SEED.nameSingular,
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
+28
-21
@@ -6,7 +6,8 @@ import { DataSource } from 'typeorm';
|
||||
|
||||
import { type DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity';
|
||||
import { CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input';
|
||||
import { FieldMetadataService } from 'src/engine/metadata-modules/field-metadata/services/field-metadata.service';
|
||||
import { FieldMetadataServiceV2 } from 'src/engine/metadata-modules/field-metadata/services/field-metadata.service-v2';
|
||||
import { ObjectMetadataServiceV2 } from 'src/engine/metadata-modules/object-metadata/object-metadata-v2.service';
|
||||
import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service';
|
||||
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import { WorkspaceMetadataCacheService } from 'src/engine/metadata-modules/workspace-metadata-cache/services/workspace-metadata-cache.service';
|
||||
@@ -30,7 +31,8 @@ import { prefillCoreViews } from 'src/engine/workspace-manager/standard-objects-
|
||||
export class DevSeederMetadataService {
|
||||
constructor(
|
||||
private readonly objectMetadataService: ObjectMetadataService,
|
||||
private readonly fieldMetadataService: FieldMetadataService,
|
||||
private readonly objectMetadataServiceV2: ObjectMetadataServiceV2,
|
||||
private readonly fieldMetadataServiceV2: FieldMetadataServiceV2,
|
||||
private readonly workspaceMetadataCacheService: WorkspaceMetadataCacheService,
|
||||
@InjectDataSource()
|
||||
private readonly coreDataSource: DataSource,
|
||||
@@ -61,7 +63,10 @@ export class DevSeederMetadataService {
|
||||
{ objectName: 'person', seeds: PERSON_CUSTOM_FIELD_SEEDS },
|
||||
],
|
||||
relations: [
|
||||
{ objectName: 'pet', seeds: PET_CUSTOM_RELATION_FIELD_SEEDS },
|
||||
{
|
||||
objectName: PET_CUSTOM_OBJECT_SEED.nameSingular,
|
||||
seeds: PET_CUSTOM_RELATION_FIELD_SEEDS,
|
||||
},
|
||||
],
|
||||
},
|
||||
[SEED_YCOMBINATOR_WORKSPACE_ID]: {
|
||||
@@ -131,9 +136,11 @@ export class DevSeederMetadataService {
|
||||
workspaceId: string;
|
||||
objectMetadataSeed: ObjectMetadataSeed;
|
||||
}): Promise<void> {
|
||||
await this.objectMetadataService.createOne({
|
||||
...objectMetadataSeed,
|
||||
dataSourceId,
|
||||
await this.objectMetadataServiceV2.createOne({
|
||||
createObjectInput: {
|
||||
...objectMetadataSeed,
|
||||
dataSourceId,
|
||||
},
|
||||
workspaceId,
|
||||
});
|
||||
}
|
||||
@@ -152,19 +159,20 @@ export class DevSeederMetadataService {
|
||||
where: { nameSingular: objectMetadataNameSingular },
|
||||
});
|
||||
|
||||
if (!objectMetadata) {
|
||||
if (!isDefined(objectMetadata)) {
|
||||
throw new Error(
|
||||
`Object metadata not found for: ${objectMetadataNameSingular}`,
|
||||
);
|
||||
}
|
||||
const createFieldInputs = fieldMetadataSeeds.map((fieldMetadataSeed) => ({
|
||||
...fieldMetadataSeed,
|
||||
objectMetadataId: objectMetadata.id,
|
||||
}));
|
||||
|
||||
await this.fieldMetadataService.createMany(
|
||||
fieldMetadataSeeds.map((fieldMetadataSeed) => ({
|
||||
...fieldMetadataSeed,
|
||||
objectMetadataId: objectMetadata.id,
|
||||
workspaceId,
|
||||
})),
|
||||
);
|
||||
await this.fieldMetadataServiceV2.createMany({
|
||||
createFieldInputs,
|
||||
workspaceId,
|
||||
});
|
||||
}
|
||||
|
||||
private async seedCoreViews({
|
||||
@@ -226,27 +234,27 @@ export class DevSeederMetadataService {
|
||||
},
|
||||
);
|
||||
|
||||
const relationFieldInputs = this.createFieldInputs({
|
||||
const createFieldInputs = this.createFieldInputs({
|
||||
relation,
|
||||
objectMetadataMaps,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
await this.fieldMetadataService.createMany(relationFieldInputs);
|
||||
await this.fieldMetadataServiceV2.createMany({
|
||||
createFieldInputs,
|
||||
workspaceId,
|
||||
});
|
||||
}
|
||||
|
||||
private createFieldInputs({
|
||||
relation,
|
||||
objectMetadataMaps,
|
||||
workspaceId,
|
||||
}: {
|
||||
relation: {
|
||||
objectName: string;
|
||||
seeds: (FieldMetadataSeed & { targetObjectMetadataNames: string[] })[];
|
||||
};
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
workspaceId: string;
|
||||
}): CreateFieldInput[] {
|
||||
}): Omit<CreateFieldInput, 'workspaceId'>[] {
|
||||
const objectMetadataId =
|
||||
objectMetadataMaps.idByNameSingular[relation.objectName];
|
||||
|
||||
@@ -261,7 +269,6 @@ export class DevSeederMetadataService {
|
||||
label: seed.label,
|
||||
name: seed.name,
|
||||
objectMetadataId,
|
||||
workspaceId,
|
||||
morphRelationsCreationPayload: seed.targetObjectMetadataNames.map(
|
||||
(targetObjectMetadataName) => {
|
||||
const targetObjectMetadataId =
|
||||
|
||||
+2
@@ -13,9 +13,11 @@ export const fromWorkspaceMigrationBuilderExceptionToValidationResponseError = (
|
||||
invalidServerlessFunction: 0,
|
||||
invalidDatabaseEventTrigger: 0,
|
||||
invalidCronTrigger: 0,
|
||||
invalidFieldMetadata: 0,
|
||||
totalErrors: 0,
|
||||
},
|
||||
errors: {
|
||||
fieldMetadata: [],
|
||||
index: [],
|
||||
objectMetadata: [],
|
||||
view: [],
|
||||
|
||||
+105
-48
@@ -1,4 +1,4 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
@@ -11,28 +11,26 @@ import {
|
||||
WorkspaceMigrationOrchestratorFailedResult,
|
||||
WorkspaceMigrationOrchestratorSuccessfulResult,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-orchestrator.type';
|
||||
import { aggregateOrchestratorActionsReport } from 'src/engine/workspace-manager/workspace-migration-v2/utils/aggregate-orchestrator-actions-report.util';
|
||||
import { WorkspaceMigrationV2CronTriggerActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/cron-trigger/workspace-migration-v2-cron-trigger-action-builder.service';
|
||||
import { WorkspaceMigrationV2DatabaseEventTriggerActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/database-event-trigger/workspace-migration-v2-database-event-trigger-actions-builder.service';
|
||||
import { WorkspaceMigrationV2FieldActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/field/services/workspace-migration-v2-field-actions-builder.service';
|
||||
import { WorkspaceMigrationV2IndexActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/index/workspace-migration-v2-index-actions-builder.service';
|
||||
import { WorkspaceMigrationV2ObjectActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/object/services/workspace-migration-v2-object-actions-builder.service';
|
||||
import { WorkspaceMigrationV2ServerlessFunctionActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/serverless-function/workspace-migration-v2-serverless-function-actions-builder.service';
|
||||
import { WorkspaceMigrationV2ViewFieldActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view-field/workspace-migration-v2-view-field-actions-builder.service';
|
||||
import { WorkspaceMigrationV2ViewActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view/workspace-migration-v2-view-actions-builder.service';
|
||||
import { WorkspaceMigrationBuilderV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/services/workspace-migration-builder-v2.service';
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceMigrationBuildOrchestratorService {
|
||||
private readonly logger = new Logger(
|
||||
WorkspaceMigrationBuildOrchestratorService.name,
|
||||
);
|
||||
|
||||
constructor(
|
||||
private readonly workspaceMigrationBuilderV2Service: WorkspaceMigrationBuilderV2Service,
|
||||
private readonly workspaceMigrationV2ObjectActionsBuilderService: WorkspaceMigrationV2ObjectActionsBuilderService,
|
||||
private readonly workspaceMigrationV2IndexActionsBuilderService: WorkspaceMigrationV2IndexActionsBuilderService,
|
||||
private readonly workspaceMigrationV2ViewActionsBuilderService: WorkspaceMigrationV2ViewActionsBuilderService,
|
||||
private readonly workspaceMigrationV2ViewFieldActionsBuilderService: WorkspaceMigrationV2ViewFieldActionsBuilderService,
|
||||
private readonly workspaceMigrationV2ServerlessFunctionActionsBuilderService: WorkspaceMigrationV2ServerlessFunctionActionsBuilderService,
|
||||
private readonly workspaceMigrationV2DatabaseEventTriggerActionsBuilderService: WorkspaceMigrationV2DatabaseEventTriggerActionsBuilderService,
|
||||
private readonly workspaceMigrationV2CronTriggerActionsBuilderService: WorkspaceMigrationV2CronTriggerActionsBuilderService,
|
||||
private readonly workspaceMigrationV2FieldActionsBuilderService: WorkspaceMigrationV2FieldActionsBuilderService,
|
||||
) {}
|
||||
|
||||
private setupOptimisticCache({
|
||||
@@ -86,6 +84,7 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
serverlessFunction: [],
|
||||
databaseEventTrigger: [],
|
||||
cronTrigger: [],
|
||||
fieldMetadata: [],
|
||||
};
|
||||
|
||||
const optimisticAllFlatEntityMaps = this.setupOptimisticCache({
|
||||
@@ -100,6 +99,7 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
flatServerlessFunctionMaps,
|
||||
flatDatabaseEventTriggerMaps,
|
||||
flatCronTriggerMaps,
|
||||
flatFieldMetadataMaps,
|
||||
} = fromToAllFlatEntityMaps;
|
||||
|
||||
if (isDefined(flatObjectMetadataMaps)) {
|
||||
@@ -107,20 +107,57 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
flatObjectMetadataMaps;
|
||||
|
||||
const objectResult =
|
||||
await this.workspaceMigrationBuilderV2Service.validateAndBuild({
|
||||
fromFlatObjectMetadataMaps,
|
||||
toFlatObjectMetadataMaps,
|
||||
buildOptions,
|
||||
});
|
||||
await this.workspaceMigrationV2ObjectActionsBuilderService.validateAndBuild(
|
||||
{
|
||||
buildOptions,
|
||||
dependencyOptimisticFlatEntityMaps: undefined,
|
||||
// TODO That's hacky also not reliable ?
|
||||
// dependencyOptimisticFlatEntityMaps: {
|
||||
// flatFieldMetadataMaps:
|
||||
// flatFieldMetadataMaps?.to ?? EMPTY_FLAT_ENTITY_MAPS,
|
||||
// },
|
||||
from: fromFlatObjectMetadataMaps,
|
||||
to: toFlatObjectMetadataMaps,
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
optimisticAllFlatEntityMaps.flatObjectMetadataMaps =
|
||||
objectResult.optimisticFlatObjectMetadataMaps;
|
||||
objectResult.optimisticFlatEntityMaps;
|
||||
|
||||
if (objectResult.status === 'fail') {
|
||||
orchestratorFailureReport.objectMetadata.push(...objectResult.errors);
|
||||
} else {
|
||||
orchestratorActionsReport.fieldMetadata = objectResult.fieldsActions;
|
||||
orchestratorActionsReport.objectMetadata = objectResult.objectActions;
|
||||
orchestratorActionsReport.objectMetadata = objectResult.actions;
|
||||
}
|
||||
}
|
||||
|
||||
if (isDefined(flatFieldMetadataMaps)) {
|
||||
const { from: fromFlatFieldMetadataMaps, to: toFlatFieldMetadataMaps } =
|
||||
flatFieldMetadataMaps;
|
||||
const fieldResult =
|
||||
await this.workspaceMigrationV2FieldActionsBuilderService.validateAndBuild(
|
||||
{
|
||||
from: fromFlatFieldMetadataMaps,
|
||||
to: toFlatFieldMetadataMaps,
|
||||
buildOptions,
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
flatObjectMetadataMaps:
|
||||
optimisticAllFlatEntityMaps.flatObjectMetadataMaps,
|
||||
},
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
optimisticAllFlatEntityMaps.flatFieldMetadataMaps =
|
||||
fieldResult.optimisticFlatEntityMaps;
|
||||
optimisticAllFlatEntityMaps.flatObjectMetadataMaps =
|
||||
fieldResult.dependencyOptimisticFlatEntityMaps.flatObjectMetadataMaps;
|
||||
|
||||
if (fieldResult.status === 'fail') {
|
||||
orchestratorFailureReport.fieldMetadata.push(...fieldResult.errors);
|
||||
} else {
|
||||
orchestratorActionsReport.fieldMetadata = fieldResult.actions;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,14 +170,19 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
to: toFlatIndexMaps,
|
||||
buildOptions,
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
flatFieldMetadataMaps:
|
||||
optimisticAllFlatEntityMaps.flatFieldMetadataMaps,
|
||||
flatObjectMetadataMaps:
|
||||
optimisticAllFlatEntityMaps.flatObjectMetadataMaps,
|
||||
},
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
optimisticAllFlatEntityMaps.flatIndexMaps =
|
||||
indexResult.optimisticFlatEntityMaps;
|
||||
optimisticAllFlatEntityMaps.flatObjectMetadataMaps =
|
||||
indexResult.dependencyOptimisticFlatEntityMaps.flatObjectMetadataMaps;
|
||||
|
||||
if (indexResult.status === 'fail') {
|
||||
orchestratorFailureReport.index.push(...indexResult.errors);
|
||||
@@ -161,6 +203,7 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
from: fromFlatViewMaps,
|
||||
to: toFlatViewMaps,
|
||||
buildOptions,
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -184,15 +227,18 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
to: toFlatViewFieldMaps,
|
||||
buildOptions,
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
flatObjectMetadataMaps:
|
||||
optimisticAllFlatEntityMaps.flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps:
|
||||
optimisticAllFlatEntityMaps.flatFieldMetadataMaps,
|
||||
flatViewMaps: optimisticAllFlatEntityMaps.flatViewMaps,
|
||||
},
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
optimisticAllFlatEntityMaps.flatViewFieldMaps =
|
||||
viewFieldResult.optimisticFlatEntityMaps;
|
||||
optimisticAllFlatEntityMaps.flatViewMaps =
|
||||
viewFieldResult.dependencyOptimisticFlatEntityMaps.flatViewMaps;
|
||||
|
||||
if (viewFieldResult.status === 'fail') {
|
||||
orchestratorFailureReport.viewField.push(...viewFieldResult.errors);
|
||||
@@ -213,7 +259,8 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
from: fromFlatServerlessFunctionMaps,
|
||||
to: toFlatServerlessFunctionMaps,
|
||||
buildOptions,
|
||||
dependencyOptimisticFlatEntityMaps: {} as AllFlatEntityMaps,
|
||||
dependencyOptimisticFlatEntityMaps: undefined,
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -242,7 +289,8 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
from: fromFlatDatabaseEventTriggerMaps,
|
||||
to: toFlatDatabaseEventTriggerMaps,
|
||||
buildOptions,
|
||||
dependencyOptimisticFlatEntityMaps: {} as AllFlatEntityMaps,
|
||||
workspaceId,
|
||||
dependencyOptimisticFlatEntityMaps: undefined,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -269,7 +317,8 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
from: fromFlatCronTriggerMaps,
|
||||
to: toFlatCronTriggerMaps,
|
||||
buildOptions,
|
||||
dependencyOptimisticFlatEntityMaps: {} as AllFlatEntityMaps,
|
||||
workspaceId,
|
||||
dependencyOptimisticFlatEntityMaps: undefined,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -292,9 +341,17 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
};
|
||||
}
|
||||
|
||||
const relatedFlatEntityMapsKeys = Object.keys(
|
||||
fromToAllFlatEntityMaps,
|
||||
) as (keyof AllFlatEntityMaps)[];
|
||||
const relatedFlatEntityMapsKeys = [
|
||||
...new Set([
|
||||
...Object.keys(fromToAllFlatEntityMaps),
|
||||
...Object.keys(dependencyAllFlatEntityMaps ?? {}),
|
||||
]),
|
||||
] as (keyof AllFlatEntityMaps)[];
|
||||
|
||||
const { aggregatedOrchestratorActionsReport } =
|
||||
aggregateOrchestratorActionsReport({
|
||||
orchestratorActionsReport,
|
||||
});
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
@@ -302,42 +359,42 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
relatedFlatEntityMapsKeys,
|
||||
actions: [
|
||||
// Object and fields and indexes
|
||||
...orchestratorActionsReport.index.deleted,
|
||||
...orchestratorActionsReport.fieldMetadata.deleted,
|
||||
...orchestratorActionsReport.objectMetadata.deleted,
|
||||
...orchestratorActionsReport.objectMetadata.created,
|
||||
...orchestratorActionsReport.objectMetadata.updated,
|
||||
...orchestratorActionsReport.fieldMetadata.created,
|
||||
...orchestratorActionsReport.fieldMetadata.updated,
|
||||
...orchestratorActionsReport.index.created,
|
||||
...orchestratorActionsReport.index.updated,
|
||||
...aggregatedOrchestratorActionsReport.index.deleted,
|
||||
...aggregatedOrchestratorActionsReport.fieldMetadata.deleted,
|
||||
...aggregatedOrchestratorActionsReport.objectMetadata.deleted,
|
||||
...aggregatedOrchestratorActionsReport.objectMetadata.created,
|
||||
...aggregatedOrchestratorActionsReport.objectMetadata.updated,
|
||||
...aggregatedOrchestratorActionsReport.fieldMetadata.created,
|
||||
...aggregatedOrchestratorActionsReport.fieldMetadata.updated,
|
||||
...aggregatedOrchestratorActionsReport.index.created,
|
||||
...aggregatedOrchestratorActionsReport.index.updated,
|
||||
///
|
||||
|
||||
// Views
|
||||
...orchestratorActionsReport.view.deleted,
|
||||
...orchestratorActionsReport.view.created,
|
||||
...orchestratorActionsReport.view.updated,
|
||||
...orchestratorActionsReport.viewField.deleted,
|
||||
...orchestratorActionsReport.viewField.created,
|
||||
...orchestratorActionsReport.viewField.updated,
|
||||
...aggregatedOrchestratorActionsReport.view.deleted,
|
||||
...aggregatedOrchestratorActionsReport.view.created,
|
||||
...aggregatedOrchestratorActionsReport.view.updated,
|
||||
...aggregatedOrchestratorActionsReport.viewField.deleted,
|
||||
...aggregatedOrchestratorActionsReport.viewField.created,
|
||||
...aggregatedOrchestratorActionsReport.viewField.updated,
|
||||
///
|
||||
|
||||
// Serverless functions
|
||||
...orchestratorActionsReport.serverlessFunction.deleted,
|
||||
...orchestratorActionsReport.serverlessFunction.created,
|
||||
...orchestratorActionsReport.serverlessFunction.updated,
|
||||
...aggregatedOrchestratorActionsReport.serverlessFunction.deleted,
|
||||
...aggregatedOrchestratorActionsReport.serverlessFunction.created,
|
||||
...aggregatedOrchestratorActionsReport.serverlessFunction.updated,
|
||||
///
|
||||
|
||||
// Database event triggers
|
||||
...orchestratorActionsReport.databaseEventTrigger.deleted,
|
||||
...orchestratorActionsReport.databaseEventTrigger.created,
|
||||
...orchestratorActionsReport.databaseEventTrigger.updated,
|
||||
...aggregatedOrchestratorActionsReport.databaseEventTrigger.deleted,
|
||||
...aggregatedOrchestratorActionsReport.databaseEventTrigger.created,
|
||||
...aggregatedOrchestratorActionsReport.databaseEventTrigger.updated,
|
||||
///
|
||||
|
||||
// Cron triggers
|
||||
...orchestratorActionsReport.cronTrigger.deleted,
|
||||
...orchestratorActionsReport.cronTrigger.created,
|
||||
...orchestratorActionsReport.cronTrigger.updated,
|
||||
...aggregatedOrchestratorActionsReport.cronTrigger.deleted,
|
||||
...aggregatedOrchestratorActionsReport.cronTrigger.created,
|
||||
...aggregatedOrchestratorActionsReport.cronTrigger.updated,
|
||||
///
|
||||
],
|
||||
workspaceId,
|
||||
|
||||
-1
@@ -11,7 +11,6 @@ import {
|
||||
WorkspaceMigrationV2ExceptionCode,
|
||||
} from 'src/engine/workspace-manager/workspace-migration.exception';
|
||||
|
||||
// TODO: replace build logic by orchestrator service
|
||||
@Injectable()
|
||||
export class WorkspaceMigrationValidateBuildAndRunService {
|
||||
private readonly logger = new Logger(
|
||||
|
||||
+3
-2
@@ -4,13 +4,13 @@ import { type AllFlatEntitiesByMetadataEngineName } from 'src/engine/core-module
|
||||
import { type AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { type FailedFlatEntityValidation } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/types/failed-flat-entity-validation.type';
|
||||
import { type CreatedDeletedUpdatedActions } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/services/workspace-entity-migration-builder-v2.service';
|
||||
import { type WorkspaceMigrationV2BuilderOptions } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/services/workspace-migration-builder-v2.service';
|
||||
import { type WorkspaceMigrationActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-action-common-v2';
|
||||
import { type WorkspaceMigrationBuilderOptions } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-builder-options.type';
|
||||
import { type WorkspaceMigrationV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-v2';
|
||||
|
||||
export type WorkspaceMigrationOrchestratorBuildArgs = {
|
||||
workspaceId: string;
|
||||
buildOptions: WorkspaceMigrationV2BuilderOptions;
|
||||
buildOptions: WorkspaceMigrationBuilderOptions;
|
||||
fromToAllFlatEntityMaps: {
|
||||
[P in keyof AllFlatEntityMaps]?: FromTo<AllFlatEntityMaps[P]>;
|
||||
};
|
||||
@@ -24,6 +24,7 @@ export type OrchestratorFailureReport = {
|
||||
};
|
||||
|
||||
export type OrchestratorActionsReport = {
|
||||
// TODO could improve this typing but making generic actions and using extract
|
||||
[P in keyof AllFlatEntitiesByMetadataEngineName]: CreatedDeletedUpdatedActions<WorkspaceMigrationActionV2>;
|
||||
} & {
|
||||
fieldMetadata: CreatedDeletedUpdatedActions<WorkspaceMigrationActionV2>;
|
||||
|
||||
+423
@@ -0,0 +1,423 @@
|
||||
import {
|
||||
eachTestingContextFilter,
|
||||
type EachTestingContext,
|
||||
} from 'twenty-shared/testing';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { getFlatFieldMetadataMock } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/get-flat-field-metadata.mock';
|
||||
import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/get-flat-object-metadata.mock';
|
||||
import { EMPTY_ORCHESTRATOR_ACTIONS_REPORT } from 'src/engine/workspace-manager/workspace-migration-v2/constant/empty-orchestrator-actions-report.constant';
|
||||
import { type OrchestratorActionsReport } from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-orchestrator.type';
|
||||
import { aggregateOrchestratorActionsReportCreateObjectAndCreateFieldActions } from 'src/engine/workspace-manager/workspace-migration-v2/utils/aggregate-orchestrator-actions-report-create-object-and-create-field-actions.util';
|
||||
import { type CreateFieldAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-field-action-v2';
|
||||
import { type CreateObjectAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-object-action-v2';
|
||||
|
||||
type CreateAggregationTestCase = EachTestingContext<{
|
||||
input: OrchestratorActionsReport;
|
||||
expected: {
|
||||
expectCreateFieldActionPerObjectMetadataId: Record<string, number>;
|
||||
expectCreateObjectActionPerObjectMetadataId: Record<string, number>;
|
||||
};
|
||||
}>;
|
||||
describe('aggregateOrchestratorActionsReportCreateObjectAndCreateFieldActions', () => {
|
||||
const testCases: CreateAggregationTestCase[] = [
|
||||
{
|
||||
title:
|
||||
'should aggregate single object with multiple fields into one object action',
|
||||
context: {
|
||||
input: {
|
||||
...EMPTY_ORCHESTRATOR_ACTIONS_REPORT,
|
||||
objectMetadata: {
|
||||
created: [
|
||||
{
|
||||
type: 'create_object',
|
||||
flatObjectMetadata: getFlatObjectMetadataMock({
|
||||
universalIdentifier: 'object-1',
|
||||
id: 'object-1',
|
||||
nameSingular: 'user',
|
||||
namePlural: 'users',
|
||||
}),
|
||||
flatFieldMetadatas: [],
|
||||
} satisfies CreateObjectAction,
|
||||
],
|
||||
updated: [],
|
||||
deleted: [],
|
||||
},
|
||||
fieldMetadata: {
|
||||
created: [
|
||||
{
|
||||
type: 'create_field',
|
||||
objectMetadataId: 'object-1',
|
||||
flatFieldMetadatas: [
|
||||
getFlatFieldMetadataMock({
|
||||
universalIdentifier: 'field-1',
|
||||
objectMetadataId: 'object-1',
|
||||
type: FieldMetadataType.TEXT,
|
||||
id: 'field-1',
|
||||
name: 'firstName',
|
||||
}),
|
||||
getFlatFieldMetadataMock({
|
||||
universalIdentifier: 'field-2',
|
||||
objectMetadataId: 'object-1',
|
||||
type: FieldMetadataType.TEXT,
|
||||
id: 'field-2',
|
||||
name: 'lastName',
|
||||
}),
|
||||
],
|
||||
} satisfies CreateFieldAction,
|
||||
],
|
||||
updated: [],
|
||||
deleted: [],
|
||||
},
|
||||
} satisfies OrchestratorActionsReport,
|
||||
expected: {
|
||||
expectCreateFieldActionPerObjectMetadataId: {},
|
||||
expectCreateObjectActionPerObjectMetadataId: {
|
||||
'object-1': 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title:
|
||||
'should keep separate field actions when no matching object action exists',
|
||||
context: {
|
||||
input: {
|
||||
...EMPTY_ORCHESTRATOR_ACTIONS_REPORT,
|
||||
objectMetadata: {
|
||||
created: [],
|
||||
updated: [],
|
||||
deleted: [],
|
||||
},
|
||||
fieldMetadata: {
|
||||
created: [
|
||||
{
|
||||
type: 'create_field',
|
||||
objectMetadataId: 'object-1',
|
||||
flatFieldMetadatas: [
|
||||
getFlatFieldMetadataMock({
|
||||
universalIdentifier: 'field-1',
|
||||
objectMetadataId: 'object-1',
|
||||
type: FieldMetadataType.TEXT,
|
||||
id: 'field-1',
|
||||
name: 'firstName',
|
||||
}),
|
||||
],
|
||||
} satisfies CreateFieldAction,
|
||||
{
|
||||
type: 'create_field',
|
||||
objectMetadataId: 'object-1',
|
||||
flatFieldMetadatas: [
|
||||
getFlatFieldMetadataMock({
|
||||
universalIdentifier: 'field-2',
|
||||
objectMetadataId: 'object-1',
|
||||
type: FieldMetadataType.TEXT,
|
||||
id: 'field-2',
|
||||
name: 'secondName',
|
||||
}),
|
||||
],
|
||||
} satisfies CreateFieldAction,
|
||||
{
|
||||
type: 'create_field',
|
||||
objectMetadataId: 'object-1',
|
||||
flatFieldMetadatas: [
|
||||
getFlatFieldMetadataMock({
|
||||
universalIdentifier: 'field-3',
|
||||
objectMetadataId: 'object-1',
|
||||
type: FieldMetadataType.TEXT,
|
||||
id: 'field-3',
|
||||
name: 'lastName',
|
||||
}),
|
||||
],
|
||||
} satisfies CreateFieldAction,
|
||||
],
|
||||
updated: [],
|
||||
deleted: [],
|
||||
},
|
||||
} satisfies OrchestratorActionsReport,
|
||||
expected: {
|
||||
expectCreateFieldActionPerObjectMetadataId: {
|
||||
'object-1': 1,
|
||||
},
|
||||
expectCreateObjectActionPerObjectMetadataId: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'should handle multiple objects with their respective fields',
|
||||
context: {
|
||||
input: {
|
||||
...EMPTY_ORCHESTRATOR_ACTIONS_REPORT,
|
||||
objectMetadata: {
|
||||
created: [
|
||||
{
|
||||
type: 'create_object',
|
||||
flatObjectMetadata: getFlatObjectMetadataMock({
|
||||
universalIdentifier: 'object-1',
|
||||
id: 'object-1',
|
||||
nameSingular: 'user',
|
||||
namePlural: 'users',
|
||||
}),
|
||||
flatFieldMetadatas: [],
|
||||
} satisfies CreateObjectAction,
|
||||
{
|
||||
type: 'create_object',
|
||||
flatObjectMetadata: getFlatObjectMetadataMock({
|
||||
universalIdentifier: 'object-2',
|
||||
id: 'object-2',
|
||||
nameSingular: 'company',
|
||||
namePlural: 'companies',
|
||||
}),
|
||||
flatFieldMetadatas: [],
|
||||
} satisfies CreateObjectAction,
|
||||
],
|
||||
updated: [],
|
||||
deleted: [],
|
||||
},
|
||||
fieldMetadata: {
|
||||
created: [
|
||||
{
|
||||
type: 'create_field',
|
||||
objectMetadataId: 'object-1',
|
||||
flatFieldMetadatas: [
|
||||
getFlatFieldMetadataMock({
|
||||
universalIdentifier: 'field-1',
|
||||
objectMetadataId: 'object-1',
|
||||
type: FieldMetadataType.TEXT,
|
||||
id: 'field-1',
|
||||
name: 'firstName',
|
||||
}),
|
||||
],
|
||||
} satisfies CreateFieldAction,
|
||||
{
|
||||
type: 'create_field',
|
||||
objectMetadataId: 'object-2',
|
||||
flatFieldMetadatas: [
|
||||
getFlatFieldMetadataMock({
|
||||
universalIdentifier: 'field-2',
|
||||
objectMetadataId: 'object-2',
|
||||
type: FieldMetadataType.TEXT,
|
||||
id: 'field-2',
|
||||
name: 'name',
|
||||
}),
|
||||
getFlatFieldMetadataMock({
|
||||
universalIdentifier: 'field-3',
|
||||
objectMetadataId: 'object-2',
|
||||
type: FieldMetadataType.TEXT,
|
||||
id: 'field-3',
|
||||
name: 'industry',
|
||||
}),
|
||||
],
|
||||
} satisfies CreateFieldAction,
|
||||
],
|
||||
updated: [],
|
||||
deleted: [],
|
||||
},
|
||||
} satisfies OrchestratorActionsReport,
|
||||
expected: {
|
||||
expectCreateFieldActionPerObjectMetadataId: {},
|
||||
expectCreateObjectActionPerObjectMetadataId: {
|
||||
'object-1': 1,
|
||||
'object-2': 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title:
|
||||
'should handle mixed scenario with some fields merged and some kept separate',
|
||||
context: {
|
||||
input: {
|
||||
...EMPTY_ORCHESTRATOR_ACTIONS_REPORT,
|
||||
objectMetadata: {
|
||||
created: [
|
||||
{
|
||||
type: 'create_object',
|
||||
flatObjectMetadata: getFlatObjectMetadataMock({
|
||||
universalIdentifier: 'object-1',
|
||||
id: 'object-1',
|
||||
nameSingular: 'user',
|
||||
namePlural: 'users',
|
||||
}),
|
||||
flatFieldMetadatas: [],
|
||||
} satisfies CreateObjectAction,
|
||||
],
|
||||
updated: [],
|
||||
deleted: [],
|
||||
},
|
||||
fieldMetadata: {
|
||||
created: [
|
||||
{
|
||||
type: 'create_field',
|
||||
objectMetadataId: 'object-1',
|
||||
flatFieldMetadatas: [
|
||||
getFlatFieldMetadataMock({
|
||||
universalIdentifier: 'field-1',
|
||||
objectMetadataId: 'object-1',
|
||||
type: FieldMetadataType.TEXT,
|
||||
id: 'field-1',
|
||||
name: 'firstName',
|
||||
}),
|
||||
],
|
||||
} satisfies CreateFieldAction,
|
||||
{
|
||||
type: 'create_field',
|
||||
objectMetadataId: 'object-2',
|
||||
flatFieldMetadatas: [
|
||||
getFlatFieldMetadataMock({
|
||||
universalIdentifier: 'field-2',
|
||||
objectMetadataId: 'object-2',
|
||||
type: FieldMetadataType.TEXT,
|
||||
id: 'field-2',
|
||||
name: 'orphanField',
|
||||
}),
|
||||
],
|
||||
} satisfies CreateFieldAction,
|
||||
],
|
||||
updated: [],
|
||||
deleted: [],
|
||||
},
|
||||
} satisfies OrchestratorActionsReport,
|
||||
expected: {
|
||||
expectCreateFieldActionPerObjectMetadataId: {
|
||||
'object-2': 1,
|
||||
},
|
||||
expectCreateObjectActionPerObjectMetadataId: {
|
||||
'object-1': 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title:
|
||||
'should aggregate multiple field actions for the same object when no create object action exists',
|
||||
context: {
|
||||
input: {
|
||||
...EMPTY_ORCHESTRATOR_ACTIONS_REPORT,
|
||||
objectMetadata: {
|
||||
created: [],
|
||||
updated: [],
|
||||
deleted: [],
|
||||
},
|
||||
fieldMetadata: {
|
||||
created: [
|
||||
{
|
||||
type: 'create_field',
|
||||
objectMetadataId: 'object-1',
|
||||
flatFieldMetadatas: [
|
||||
getFlatFieldMetadataMock({
|
||||
universalIdentifier: 'field-1',
|
||||
objectMetadataId: 'object-1',
|
||||
type: FieldMetadataType.TEXT,
|
||||
id: 'field-1',
|
||||
name: 'firstName',
|
||||
}),
|
||||
],
|
||||
} satisfies CreateFieldAction,
|
||||
{
|
||||
type: 'create_field',
|
||||
objectMetadataId: 'object-1',
|
||||
flatFieldMetadatas: [
|
||||
getFlatFieldMetadataMock({
|
||||
universalIdentifier: 'field-2',
|
||||
objectMetadataId: 'object-1',
|
||||
type: FieldMetadataType.TEXT,
|
||||
id: 'field-2',
|
||||
name: 'lastName',
|
||||
}),
|
||||
],
|
||||
} satisfies CreateFieldAction,
|
||||
{
|
||||
type: 'create_field',
|
||||
objectMetadataId: 'object-1',
|
||||
flatFieldMetadatas: [
|
||||
getFlatFieldMetadataMock({
|
||||
universalIdentifier: 'field-3',
|
||||
objectMetadataId: 'object-1',
|
||||
type: FieldMetadataType.TEXT,
|
||||
id: 'field-3',
|
||||
name: 'email',
|
||||
}),
|
||||
],
|
||||
} satisfies CreateFieldAction,
|
||||
],
|
||||
updated: [],
|
||||
deleted: [],
|
||||
},
|
||||
} satisfies OrchestratorActionsReport,
|
||||
expected: {
|
||||
expectCreateFieldActionPerObjectMetadataId: {
|
||||
'object-1': 1,
|
||||
},
|
||||
expectCreateObjectActionPerObjectMetadataId: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'should handle empty actions report',
|
||||
context: {
|
||||
input: EMPTY_ORCHESTRATOR_ACTIONS_REPORT,
|
||||
expected: {
|
||||
expectCreateFieldActionPerObjectMetadataId: {},
|
||||
expectCreateObjectActionPerObjectMetadataId: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
test.each(eachTestingContextFilter(testCases))(
|
||||
'$title',
|
||||
({ context: { input, expected } }) => {
|
||||
const result =
|
||||
aggregateOrchestratorActionsReportCreateObjectAndCreateFieldActions({
|
||||
orchestratorActionsReport: input,
|
||||
});
|
||||
|
||||
const fieldActions = result.fieldMetadata.created as CreateFieldAction[];
|
||||
const fieldActionCounts = fieldActions.reduce(
|
||||
(acc, action) => {
|
||||
acc[action.objectMetadataId] =
|
||||
(acc[action.objectMetadataId] || 0) + 1;
|
||||
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, number>,
|
||||
);
|
||||
|
||||
const objectActions = result.objectMetadata
|
||||
.created as CreateObjectAction[];
|
||||
const objectActionCounts = objectActions.reduce(
|
||||
(acc, action) => {
|
||||
acc[action.flatObjectMetadata.id] =
|
||||
(acc[action.flatObjectMetadata.id] || 0) + 1;
|
||||
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, number>,
|
||||
);
|
||||
|
||||
Object.entries(
|
||||
expected.expectCreateFieldActionPerObjectMetadataId,
|
||||
).forEach(([objectId, expectedCount]) => {
|
||||
expect(fieldActionCounts[objectId]).toBe(expectedCount);
|
||||
});
|
||||
|
||||
Object.entries(
|
||||
expected.expectCreateObjectActionPerObjectMetadataId,
|
||||
).forEach(([objectId, expectedCount]) => {
|
||||
expect(objectActionCounts[objectId]).toBe(expectedCount);
|
||||
});
|
||||
|
||||
// Check total counts
|
||||
const expectedTotalFieldActions = Object.values(
|
||||
expected.expectCreateFieldActionPerObjectMetadataId,
|
||||
).reduce((sum, count) => sum + count, 0);
|
||||
const expectedTotalObjectActions = Object.values(
|
||||
expected.expectCreateObjectActionPerObjectMetadataId,
|
||||
).reduce((sum, count) => sum + count, 0);
|
||||
|
||||
expect(fieldActions).toHaveLength(expectedTotalFieldActions);
|
||||
expect(objectActions).toHaveLength(expectedTotalObjectActions);
|
||||
},
|
||||
);
|
||||
});
|
||||
+254
@@ -0,0 +1,254 @@
|
||||
import {
|
||||
eachTestingContextFilter,
|
||||
type EachTestingContext,
|
||||
} from 'twenty-shared/testing';
|
||||
|
||||
import { EMPTY_ORCHESTRATOR_ACTIONS_REPORT } from 'src/engine/workspace-manager/workspace-migration-v2/constant/empty-orchestrator-actions-report.constant';
|
||||
import { type OrchestratorActionsReport } from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-orchestrator.type';
|
||||
import { aggregateOrchestratorActionsReportDeleteObjectAndDeleteFieldActions } from 'src/engine/workspace-manager/workspace-migration-v2/utils/aggregate-orchestrator-actions-report-delete-object-and-delete-field.util';
|
||||
import { type DeleteFieldAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-field-action-v2';
|
||||
import { type DeleteObjectAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-object-action-v2';
|
||||
|
||||
type DeleteAggregationTestCase = EachTestingContext<{
|
||||
input: OrchestratorActionsReport;
|
||||
expected: {
|
||||
expectDeleteFieldActionPerObjectMetadataId: Record<string, number>;
|
||||
expectDeleteObjectActionPerObjectMetadataId: Record<string, number>;
|
||||
};
|
||||
}>;
|
||||
|
||||
describe('aggregateOrchestratorActionsReportDeleteObjectAndDeleteFieldActions', () => {
|
||||
const testCases: DeleteAggregationTestCase[] = [
|
||||
{
|
||||
title: 'should remove field actions when parent object is being deleted',
|
||||
context: {
|
||||
input: {
|
||||
...EMPTY_ORCHESTRATOR_ACTIONS_REPORT,
|
||||
objectMetadata: {
|
||||
created: [],
|
||||
updated: [],
|
||||
deleted: [
|
||||
{
|
||||
type: 'delete_object',
|
||||
objectMetadataId: 'object-1',
|
||||
} satisfies DeleteObjectAction,
|
||||
],
|
||||
},
|
||||
fieldMetadata: {
|
||||
created: [],
|
||||
updated: [],
|
||||
deleted: [
|
||||
{
|
||||
type: 'delete_field',
|
||||
fieldMetadataId: 'field-1',
|
||||
objectMetadataId: 'object-1',
|
||||
} satisfies DeleteFieldAction,
|
||||
{
|
||||
type: 'delete_field',
|
||||
fieldMetadataId: 'field-2',
|
||||
objectMetadataId: 'object-1',
|
||||
} satisfies DeleteFieldAction,
|
||||
],
|
||||
},
|
||||
} satisfies OrchestratorActionsReport,
|
||||
expected: {
|
||||
expectDeleteFieldActionPerObjectMetadataId: {},
|
||||
expectDeleteObjectActionPerObjectMetadataId: {
|
||||
'object-1': 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'should keep field actions when no parent object is being deleted',
|
||||
context: {
|
||||
input: {
|
||||
...EMPTY_ORCHESTRATOR_ACTIONS_REPORT,
|
||||
objectMetadata: {
|
||||
created: [],
|
||||
updated: [],
|
||||
deleted: [],
|
||||
},
|
||||
fieldMetadata: {
|
||||
created: [],
|
||||
updated: [],
|
||||
deleted: [
|
||||
{
|
||||
type: 'delete_field',
|
||||
fieldMetadataId: 'field-1',
|
||||
objectMetadataId: 'object-1',
|
||||
} satisfies DeleteFieldAction,
|
||||
],
|
||||
},
|
||||
} satisfies OrchestratorActionsReport,
|
||||
expected: {
|
||||
expectDeleteFieldActionPerObjectMetadataId: {
|
||||
'object-1': 1,
|
||||
},
|
||||
expectDeleteObjectActionPerObjectMetadataId: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title:
|
||||
'should handle mixed scenario with some fields removed and some kept',
|
||||
context: {
|
||||
input: {
|
||||
...EMPTY_ORCHESTRATOR_ACTIONS_REPORT,
|
||||
objectMetadata: {
|
||||
created: [],
|
||||
updated: [],
|
||||
deleted: [
|
||||
{
|
||||
type: 'delete_object',
|
||||
objectMetadataId: 'object-1',
|
||||
} satisfies DeleteObjectAction,
|
||||
],
|
||||
},
|
||||
fieldMetadata: {
|
||||
created: [],
|
||||
updated: [],
|
||||
deleted: [
|
||||
{
|
||||
type: 'delete_field',
|
||||
fieldMetadataId: 'field-1',
|
||||
objectMetadataId: 'object-1',
|
||||
} satisfies DeleteFieldAction,
|
||||
{
|
||||
type: 'delete_field',
|
||||
fieldMetadataId: 'field-2',
|
||||
objectMetadataId: 'object-2',
|
||||
} satisfies DeleteFieldAction,
|
||||
],
|
||||
},
|
||||
} satisfies OrchestratorActionsReport,
|
||||
expected: {
|
||||
expectDeleteFieldActionPerObjectMetadataId: {
|
||||
'object-2': 1,
|
||||
},
|
||||
expectDeleteObjectActionPerObjectMetadataId: {
|
||||
'object-1': 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'should handle multiple objects with mixed field deletions',
|
||||
context: {
|
||||
input: {
|
||||
...EMPTY_ORCHESTRATOR_ACTIONS_REPORT,
|
||||
objectMetadata: {
|
||||
created: [],
|
||||
updated: [],
|
||||
deleted: [
|
||||
{
|
||||
type: 'delete_object',
|
||||
objectMetadataId: 'object-1',
|
||||
} satisfies DeleteObjectAction,
|
||||
{
|
||||
type: 'delete_object',
|
||||
objectMetadataId: 'object-2',
|
||||
} satisfies DeleteObjectAction,
|
||||
],
|
||||
},
|
||||
fieldMetadata: {
|
||||
created: [],
|
||||
updated: [],
|
||||
deleted: [
|
||||
{
|
||||
type: 'delete_field',
|
||||
fieldMetadataId: 'field-1',
|
||||
objectMetadataId: 'object-1',
|
||||
} satisfies DeleteFieldAction,
|
||||
{
|
||||
type: 'delete_field',
|
||||
fieldMetadataId: 'field-2',
|
||||
objectMetadataId: 'object-2',
|
||||
} satisfies DeleteFieldAction,
|
||||
{
|
||||
type: 'delete_field',
|
||||
fieldMetadataId: 'field-3',
|
||||
objectMetadataId: 'object-3',
|
||||
} satisfies DeleteFieldAction,
|
||||
],
|
||||
},
|
||||
} satisfies OrchestratorActionsReport,
|
||||
expected: {
|
||||
expectDeleteFieldActionPerObjectMetadataId: {
|
||||
'object-3': 1,
|
||||
},
|
||||
expectDeleteObjectActionPerObjectMetadataId: {
|
||||
'object-1': 1,
|
||||
'object-2': 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'should handle empty actions report',
|
||||
context: {
|
||||
input: EMPTY_ORCHESTRATOR_ACTIONS_REPORT,
|
||||
expected: {
|
||||
expectDeleteFieldActionPerObjectMetadataId: {},
|
||||
expectDeleteObjectActionPerObjectMetadataId: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
test.each(eachTestingContextFilter(testCases))(
|
||||
'$title',
|
||||
({ context: { input, expected } }) => {
|
||||
const result =
|
||||
aggregateOrchestratorActionsReportDeleteObjectAndDeleteFieldActions({
|
||||
orchestratorActionsReport: input,
|
||||
});
|
||||
|
||||
const fieldActions = result.fieldMetadata.deleted as DeleteFieldAction[];
|
||||
const fieldActionCounts = fieldActions.reduce(
|
||||
(acc, action) => {
|
||||
acc[action.objectMetadataId] =
|
||||
(acc[action.objectMetadataId] || 0) + 1;
|
||||
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, number>,
|
||||
);
|
||||
|
||||
const objectActions = result.objectMetadata
|
||||
.deleted as DeleteObjectAction[];
|
||||
const objectActionCounts = objectActions.reduce(
|
||||
(acc, action) => {
|
||||
acc[action.objectMetadataId] =
|
||||
(acc[action.objectMetadataId] || 0) + 1;
|
||||
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, number>,
|
||||
);
|
||||
|
||||
Object.entries(
|
||||
expected.expectDeleteFieldActionPerObjectMetadataId,
|
||||
).forEach(([objectId, expectedCount]) => {
|
||||
expect(fieldActionCounts[objectId]).toBe(expectedCount);
|
||||
});
|
||||
|
||||
Object.entries(
|
||||
expected.expectDeleteObjectActionPerObjectMetadataId,
|
||||
).forEach(([objectId, expectedCount]) => {
|
||||
expect(objectActionCounts[objectId]).toBe(expectedCount);
|
||||
});
|
||||
|
||||
// Check total counts
|
||||
const expectedTotalFieldActions = Object.values(
|
||||
expected.expectDeleteFieldActionPerObjectMetadataId,
|
||||
).reduce((sum, count) => sum + count, 0);
|
||||
const expectedTotalObjectActions = Object.values(
|
||||
expected.expectDeleteObjectActionPerObjectMetadataId,
|
||||
).reduce((sum, count) => sum + count, 0);
|
||||
|
||||
expect(fieldActions).toHaveLength(expectedTotalFieldActions);
|
||||
expect(objectActions).toHaveLength(expectedTotalObjectActions);
|
||||
},
|
||||
);
|
||||
});
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type OrchestratorActionsReport } from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-orchestrator.type';
|
||||
import { type CreateFieldAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-field-action-v2';
|
||||
import { type CreateObjectAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-object-action-v2';
|
||||
|
||||
type AggregateOrchestratorActionsReportCreateObjectAndCreateFieldActionsArgs = {
|
||||
orchestratorActionsReport: OrchestratorActionsReport;
|
||||
};
|
||||
|
||||
type AggregatedActions = {
|
||||
createdFieldActionByObjectMetadataId: Record<string, CreateFieldAction>;
|
||||
createdObjectActionByObjectMetadataId: Record<string, CreateObjectAction>;
|
||||
};
|
||||
export const aggregateOrchestratorActionsReportCreateObjectAndCreateFieldActions =
|
||||
({
|
||||
orchestratorActionsReport,
|
||||
}: AggregateOrchestratorActionsReportCreateObjectAndCreateFieldActionsArgs): OrchestratorActionsReport => {
|
||||
const initialCreatedObjectActionByObjectMetadataId = (
|
||||
orchestratorActionsReport.objectMetadata.created as CreateObjectAction[]
|
||||
).reduce(
|
||||
(acc, createObjectAction) => ({
|
||||
...acc,
|
||||
[createObjectAction.flatObjectMetadata.id]: createObjectAction,
|
||||
}),
|
||||
{},
|
||||
);
|
||||
const initialAccumulator: AggregatedActions = {
|
||||
createdFieldActionByObjectMetadataId: {},
|
||||
createdObjectActionByObjectMetadataId:
|
||||
initialCreatedObjectActionByObjectMetadataId,
|
||||
};
|
||||
|
||||
const {
|
||||
createdFieldActionByObjectMetadataId,
|
||||
createdObjectActionByObjectMetadataId,
|
||||
} = (
|
||||
orchestratorActionsReport.fieldMetadata.created as CreateFieldAction[]
|
||||
).reduce<AggregatedActions>(
|
||||
(
|
||||
{
|
||||
createdObjectActionByObjectMetadataId,
|
||||
createdFieldActionByObjectMetadataId,
|
||||
},
|
||||
createFieldAction,
|
||||
) => {
|
||||
const existingCreateObjectAction =
|
||||
createdObjectActionByObjectMetadataId[
|
||||
createFieldAction.objectMetadataId
|
||||
];
|
||||
|
||||
if (isDefined(existingCreateObjectAction)) {
|
||||
return {
|
||||
createdObjectActionByObjectMetadataId: {
|
||||
...createdObjectActionByObjectMetadataId,
|
||||
[createFieldAction.objectMetadataId]: {
|
||||
...existingCreateObjectAction,
|
||||
flatFieldMetadatas: [
|
||||
...existingCreateObjectAction.flatFieldMetadatas,
|
||||
...createFieldAction.flatFieldMetadatas,
|
||||
],
|
||||
},
|
||||
},
|
||||
createdFieldActionByObjectMetadataId,
|
||||
};
|
||||
}
|
||||
|
||||
const existingCreateFieldAction =
|
||||
createdFieldActionByObjectMetadataId[
|
||||
createFieldAction.objectMetadataId
|
||||
];
|
||||
|
||||
if (isDefined(existingCreateFieldAction)) {
|
||||
return {
|
||||
createdFieldActionByObjectMetadataId: {
|
||||
...createdFieldActionByObjectMetadataId,
|
||||
[createFieldAction.objectMetadataId]: {
|
||||
...existingCreateFieldAction,
|
||||
flatFieldMetadatas: [
|
||||
...existingCreateFieldAction.flatFieldMetadatas,
|
||||
...createFieldAction.flatFieldMetadatas,
|
||||
],
|
||||
},
|
||||
},
|
||||
createdObjectActionByObjectMetadataId,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
createdFieldActionByObjectMetadataId: {
|
||||
...createdFieldActionByObjectMetadataId,
|
||||
[createFieldAction.objectMetadataId]: createFieldAction,
|
||||
},
|
||||
createdObjectActionByObjectMetadataId,
|
||||
};
|
||||
},
|
||||
initialAccumulator,
|
||||
);
|
||||
|
||||
return {
|
||||
...orchestratorActionsReport,
|
||||
fieldMetadata: {
|
||||
...orchestratorActionsReport.fieldMetadata,
|
||||
created: Object.values(createdFieldActionByObjectMetadataId),
|
||||
},
|
||||
objectMetadata: {
|
||||
...orchestratorActionsReport.objectMetadata,
|
||||
created: Object.values(createdObjectActionByObjectMetadataId),
|
||||
},
|
||||
};
|
||||
};
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type OrchestratorActionsReport } from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-orchestrator.type';
|
||||
import { type DeleteFieldAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-field-action-v2';
|
||||
import { type DeleteObjectAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-object-action-v2';
|
||||
|
||||
type AggregateOrchestratorActionsReportDeleteObjectAndDeleteFieldActionsArgs = {
|
||||
orchestratorActionsReport: OrchestratorActionsReport;
|
||||
};
|
||||
|
||||
type AggregatedActions = {
|
||||
deleteFieldActionByFieldMetadataId: Record<string, DeleteFieldAction>;
|
||||
};
|
||||
export const aggregateOrchestratorActionsReportDeleteObjectAndDeleteFieldActions =
|
||||
({
|
||||
orchestratorActionsReport,
|
||||
}: AggregateOrchestratorActionsReportDeleteObjectAndDeleteFieldActionsArgs): OrchestratorActionsReport => {
|
||||
const deleteObjectActionByObjectMetadataId = (
|
||||
orchestratorActionsReport.objectMetadata.deleted as DeleteObjectAction[]
|
||||
).reduce<Record<string, DeleteObjectAction>>(
|
||||
(acc, deleteObjectAction) => ({
|
||||
...acc,
|
||||
[deleteObjectAction.objectMetadataId]: deleteObjectAction,
|
||||
}),
|
||||
{},
|
||||
);
|
||||
const initialAccumulator: AggregatedActions = {
|
||||
deleteFieldActionByFieldMetadataId: {},
|
||||
};
|
||||
|
||||
const { deleteFieldActionByFieldMetadataId } = (
|
||||
orchestratorActionsReport.fieldMetadata.deleted as DeleteFieldAction[]
|
||||
).reduce<AggregatedActions>(
|
||||
({ deleteFieldActionByFieldMetadataId }, deleteFieldAction) => {
|
||||
const fieldParentObjectDeleteObjectAction =
|
||||
deleteObjectActionByObjectMetadataId[
|
||||
deleteFieldAction.objectMetadataId
|
||||
];
|
||||
|
||||
if (isDefined(fieldParentObjectDeleteObjectAction)) {
|
||||
return {
|
||||
deleteFieldActionByFieldMetadataId,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
deleteFieldActionByFieldMetadataId: {
|
||||
...deleteFieldActionByFieldMetadataId,
|
||||
[deleteFieldAction.fieldMetadataId]: deleteFieldAction,
|
||||
},
|
||||
};
|
||||
},
|
||||
initialAccumulator,
|
||||
);
|
||||
|
||||
return {
|
||||
...orchestratorActionsReport,
|
||||
fieldMetadata: {
|
||||
...orchestratorActionsReport.fieldMetadata,
|
||||
deleted: Object.values(deleteFieldActionByFieldMetadataId),
|
||||
},
|
||||
};
|
||||
};
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import { type OrchestratorActionsReport } from 'src/engine/workspace-manager/workspace-migration-v2/types/workspace-migration-orchestrator.type';
|
||||
import { aggregateOrchestratorActionsReportCreateObjectAndCreateFieldActions } from 'src/engine/workspace-manager/workspace-migration-v2/utils/aggregate-orchestrator-actions-report-create-object-and-create-field-actions.util';
|
||||
import { aggregateOrchestratorActionsReportDeleteObjectAndDeleteFieldActions } from 'src/engine/workspace-manager/workspace-migration-v2/utils/aggregate-orchestrator-actions-report-delete-object-and-delete-field.util';
|
||||
|
||||
type AggregateOrchestratorActionsReportArgs = {
|
||||
orchestratorActionsReport: OrchestratorActionsReport;
|
||||
};
|
||||
export const aggregateOrchestratorActionsReport = ({
|
||||
orchestratorActionsReport,
|
||||
}: AggregateOrchestratorActionsReportArgs) => {
|
||||
const aggregatedOrchestratorActionsReport = [
|
||||
aggregateOrchestratorActionsReportCreateObjectAndCreateFieldActions,
|
||||
aggregateOrchestratorActionsReportDeleteObjectAndDeleteFieldActions,
|
||||
].reduce(
|
||||
(currentOrchestratorActionsReport, aggregator) =>
|
||||
aggregator({
|
||||
orchestratorActionsReport: currentOrchestratorActionsReport,
|
||||
}),
|
||||
orchestratorActionsReport,
|
||||
);
|
||||
|
||||
return { aggregatedOrchestratorActionsReport };
|
||||
};
|
||||
-56
@@ -1,56 +0,0 @@
|
||||
import { type FromTo } from 'twenty-shared/types';
|
||||
|
||||
export type DeletedCreatedUpdatedMatrix<T> = {
|
||||
created: T[];
|
||||
deleted: T[];
|
||||
updated: FromTo<T>[];
|
||||
};
|
||||
|
||||
export type CustomDeletedCreatedUpdatedMatrix<TLabel extends string, TInput> = {
|
||||
[P in keyof DeletedCreatedUpdatedMatrix<TInput> as `${P}${Capitalize<TLabel>}`]: DeletedCreatedUpdatedMatrix<TInput>[P];
|
||||
};
|
||||
|
||||
export type UniversalIdentifierItem = {
|
||||
universalIdentifier: string;
|
||||
};
|
||||
|
||||
export const deletedCreatedUpdatedMatrixDispatcher = <
|
||||
T extends UniversalIdentifierItem,
|
||||
>({
|
||||
from,
|
||||
to,
|
||||
}: FromTo<T[]>): DeletedCreatedUpdatedMatrix<T> => {
|
||||
const initialDispatcher: DeletedCreatedUpdatedMatrix<T> = {
|
||||
created: [],
|
||||
updated: [],
|
||||
deleted: [],
|
||||
};
|
||||
|
||||
const fromMap = new Map(from.map((obj) => [obj.universalIdentifier, obj]));
|
||||
const toMap = new Map(to.map((obj) => [obj.universalIdentifier, obj]));
|
||||
|
||||
for (const [identifier, fromObj] of fromMap) {
|
||||
if (!toMap.has(identifier)) {
|
||||
initialDispatcher.deleted.push(fromObj);
|
||||
}
|
||||
}
|
||||
|
||||
for (const [identifier, toObj] of toMap) {
|
||||
if (!fromMap.has(identifier)) {
|
||||
initialDispatcher.created.push(toObj);
|
||||
}
|
||||
}
|
||||
|
||||
for (const [identifier, fromObj] of fromMap) {
|
||||
const toObj = toMap.get(identifier);
|
||||
|
||||
if (toObj) {
|
||||
initialDispatcher.updated.push({
|
||||
from: fromObj,
|
||||
to: toObj,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return initialDispatcher;
|
||||
};
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
import { type FromTo } from 'twenty-shared/types';
|
||||
|
||||
import { EMPTY_FLAT_ENTITY_MAPS } from 'src/engine/core-modules/common/constant/empty-flat-entity-maps.constant';
|
||||
import { type AllFlatEntities } from 'src/engine/core-modules/common/types/all-flat-entities.type';
|
||||
import { type FlatEntityMaps } from 'src/engine/core-modules/common/types/flat-entity-maps.type';
|
||||
import { addFlatEntityToFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/add-flat-entity-to-flat-entity-maps-or-throw.util';
|
||||
import { type WorkspaceMigrationBuilderOptions } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-builder-options.type';
|
||||
|
||||
export type DeletedCreatedUpdatedMatrix<T extends AllFlatEntities> = {
|
||||
createdFlatEntityMaps: FlatEntityMaps<T>;
|
||||
deletedFlatEntityMaps: FlatEntityMaps<T>;
|
||||
updatedFlatEntityMaps: FromTo<FlatEntityMaps<T>>;
|
||||
};
|
||||
|
||||
export type UniversalIdentifierItem = {
|
||||
universalIdentifier: string;
|
||||
};
|
||||
|
||||
export const flatEntityDeletedCreatedUpdatedMatrixDispatcher = <
|
||||
T extends AllFlatEntities,
|
||||
>({
|
||||
from,
|
||||
to,
|
||||
buildOptions,
|
||||
}: FromTo<T[]> & {
|
||||
buildOptions: WorkspaceMigrationBuilderOptions;
|
||||
}): DeletedCreatedUpdatedMatrix<T> => {
|
||||
const initialDispatcher: DeletedCreatedUpdatedMatrix<T> = {
|
||||
createdFlatEntityMaps: EMPTY_FLAT_ENTITY_MAPS,
|
||||
deletedFlatEntityMaps: EMPTY_FLAT_ENTITY_MAPS,
|
||||
updatedFlatEntityMaps: {
|
||||
from: EMPTY_FLAT_ENTITY_MAPS,
|
||||
to: EMPTY_FLAT_ENTITY_MAPS,
|
||||
},
|
||||
};
|
||||
|
||||
const fromMap = new Map(from.map((obj) => [obj.universalIdentifier, obj]));
|
||||
const toMap = new Map(to.map((obj) => [obj.universalIdentifier, obj]));
|
||||
|
||||
if (buildOptions.inferDeletionFromMissingEntities) {
|
||||
for (const [universalIdentifier, fromEntity] of fromMap) {
|
||||
if (!toMap.has(universalIdentifier)) {
|
||||
initialDispatcher.deletedFlatEntityMaps =
|
||||
addFlatEntityToFlatEntityMapsOrThrow({
|
||||
flatEntity: fromEntity,
|
||||
flatEntityMaps: initialDispatcher.deletedFlatEntityMaps,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const [universalIdentifier, toFlatEntity] of toMap) {
|
||||
if (!fromMap.has(universalIdentifier)) {
|
||||
initialDispatcher.createdFlatEntityMaps =
|
||||
addFlatEntityToFlatEntityMapsOrThrow({
|
||||
flatEntity: toFlatEntity,
|
||||
flatEntityMaps: initialDispatcher.createdFlatEntityMaps,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (const [universalIdentifier, fromFlatEntity] of fromMap) {
|
||||
const toFlatEntity = toMap.get(universalIdentifier);
|
||||
|
||||
// TODO Perf improvement compare directly here to avoid mapping the whole workspaces + avoid compare duplication
|
||||
// Would not have FromTo anymore but an PropertyUpdates[]
|
||||
if (toFlatEntity) {
|
||||
initialDispatcher.updatedFlatEntityMaps.from =
|
||||
addFlatEntityToFlatEntityMapsOrThrow({
|
||||
flatEntity: fromFlatEntity,
|
||||
flatEntityMaps: initialDispatcher.updatedFlatEntityMaps.from,
|
||||
});
|
||||
|
||||
initialDispatcher.updatedFlatEntityMaps.to =
|
||||
addFlatEntityToFlatEntityMapsOrThrow({
|
||||
flatEntity: toFlatEntity,
|
||||
flatEntityMaps: initialDispatcher.updatedFlatEntityMaps.to,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return initialDispatcher;
|
||||
};
|
||||
-2535
File diff suppressed because it is too large
Load Diff
-227
@@ -1,227 +0,0 @@
|
||||
import { faker } from '@faker-js/faker';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { removePropertiesFromRecord } from 'twenty-shared/utils';
|
||||
|
||||
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
|
||||
|
||||
import { COMPANY_FLAT_FIELDS_MOCK } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/company-flat-fields.mock';
|
||||
import { getFlatFieldMetadataMock } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/get-flat-field-metadata.mock';
|
||||
import { getRelationTargetFlatFieldMetadataMock } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/get-morph-or-relation-target-flat-field-metadata-mock';
|
||||
import { OPPORTUNITY_FLAT_FIELDS_MOCK } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/opportunity-flat-fields.mock';
|
||||
import { PET_FLAT_FIELDS_MOCK } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/pet-flat-fields.mock';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { FLAT_OBJECT_METADATA_MAPS_MOCKS } from 'src/engine/metadata-modules/flat-object-metadata-maps/mocks/flat-object-metadata-maps.mock';
|
||||
import { addFlatFieldMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/add-flat-field-metadata-in-flat-object-metadata-maps-or-throw.util';
|
||||
import { deleteFieldFromFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/delete-field-from-flat-object-metadata-maps-or-throw.util';
|
||||
import { replaceFlatFieldMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/replace-flat-field-metadata-in-flat-object-metadata-maps-or-throw.util';
|
||||
import { PET_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/pet-flat-object.mock';
|
||||
import { ROCKET_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/rocket-flat-object.mock';
|
||||
import { type WorkspaceMigrationBuilderTestCase } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/types/workspace-migration-builder-test-case.type';
|
||||
import { fromFlatObjectMetadataToFlatObjectMetadataWithoutFields } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/from-flat-object-metadata-to-flat-object-metadata-without-fields.util';
|
||||
|
||||
const relationTestCases: WorkspaceMigrationBuilderTestCase[] = [
|
||||
{
|
||||
title: 'It should build an create_field action for a RELATION field',
|
||||
context: {
|
||||
input: () => {
|
||||
const targetRelationId = faker.string.uuid();
|
||||
const sourceRelationId = faker.string.uuid();
|
||||
const draftSourceRelationFlatField =
|
||||
getRelationTargetFlatFieldMetadataMock({
|
||||
id: sourceRelationId,
|
||||
universalIdentifier: 'field-metadata-unique-identifier-1',
|
||||
objectMetadataId: PET_FLAT_OBJECT_MOCK.id,
|
||||
type: FieldMetadataType.RELATION,
|
||||
settings: {
|
||||
relationType: RelationType.MANY_TO_ONE,
|
||||
joinColumnName: 'whatever',
|
||||
onDelete: undefined,
|
||||
},
|
||||
relationTargetFieldMetadataId: targetRelationId,
|
||||
relationTargetObjectMetadataId: ROCKET_FLAT_OBJECT_MOCK.id,
|
||||
flatRelationTargetObjectMetadata:
|
||||
fromFlatObjectMetadataToFlatObjectMetadataWithoutFields(
|
||||
ROCKET_FLAT_OBJECT_MOCK,
|
||||
),
|
||||
});
|
||||
|
||||
const targetRelationFlatField = getFlatFieldMetadataMock({
|
||||
id: targetRelationId,
|
||||
universalIdentifier: 'field-metadata-unique-identifier-2',
|
||||
objectMetadataId: ROCKET_FLAT_OBJECT_MOCK.id,
|
||||
type: FieldMetadataType.RELATION,
|
||||
settings: {
|
||||
relationType: RelationType.ONE_TO_MANY,
|
||||
joinColumnName: 'sourceWhatever',
|
||||
onDelete: undefined,
|
||||
},
|
||||
relationTargetFieldMetadataId: sourceRelationId,
|
||||
relationTargetObjectMetadataId: PET_FLAT_OBJECT_MOCK.id,
|
||||
flatRelationTargetFieldMetadata: draftSourceRelationFlatField,
|
||||
flatRelationTargetObjectMetadata:
|
||||
fromFlatObjectMetadataToFlatObjectMetadataWithoutFields(
|
||||
PET_FLAT_OBJECT_MOCK,
|
||||
),
|
||||
});
|
||||
|
||||
const sourceRelationFlatField = getFlatFieldMetadataMock({
|
||||
...draftSourceRelationFlatField,
|
||||
flatRelationTargetFieldMetadata: removePropertiesFromRecord(
|
||||
targetRelationFlatField as FlatFieldMetadata<FieldMetadataType.RELATION>,
|
||||
[
|
||||
'flatRelationTargetFieldMetadata',
|
||||
'flatRelationTargetObjectMetadata',
|
||||
],
|
||||
),
|
||||
});
|
||||
const toFlatObjectMetadataMaps = [
|
||||
sourceRelationFlatField,
|
||||
targetRelationFlatField,
|
||||
].reduce((flatObjectMetadataMaps, flatFieldMetadata) => {
|
||||
return addFlatFieldMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatFieldMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
});
|
||||
}, FLAT_OBJECT_METADATA_MAPS_MOCKS);
|
||||
|
||||
return {
|
||||
fromFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
toFlatObjectMetadataMaps,
|
||||
};
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'It should build an update_field action for a RELATION field',
|
||||
context: {
|
||||
input: {
|
||||
fromFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
toFlatObjectMetadataMaps:
|
||||
replaceFlatFieldMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatFieldMetadata: getFlatFieldMetadataMock({
|
||||
...PET_FLAT_FIELDS_MOCK.species,
|
||||
description: 'new description',
|
||||
label: 'new label',
|
||||
}),
|
||||
flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title:
|
||||
'It should NOT build an update_field action for a field RELATION uncovered fields mutation',
|
||||
context: {
|
||||
input: {
|
||||
fromFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
toFlatObjectMetadataMaps:
|
||||
replaceFlatFieldMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatFieldMetadata: getFlatFieldMetadataMock({
|
||||
...COMPANY_FLAT_FIELDS_MOCK.opportunities,
|
||||
settings: {
|
||||
relationType: RelationType.MANY_TO_ONE,
|
||||
joinColumnName: 'new-column-name',
|
||||
onDelete: undefined,
|
||||
},
|
||||
name: 'newName',
|
||||
relationTargetFieldMetadataId: faker.string.uuid(),
|
||||
relationTargetObjectMetadataId: faker.string.uuid(),
|
||||
}),
|
||||
flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const basicCrudTestCases: WorkspaceMigrationBuilderTestCase[] = [
|
||||
{
|
||||
title: 'It should build an create_field action',
|
||||
context: {
|
||||
input: {
|
||||
fromFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
toFlatObjectMetadataMaps:
|
||||
addFlatFieldMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatFieldMetadata: getFlatFieldMetadataMock({
|
||||
universalIdentifier: 'field-metadata-unique-identifier-1',
|
||||
type: FieldMetadataType.TEXT,
|
||||
objectMetadataId: ROCKET_FLAT_OBJECT_MOCK.id,
|
||||
}),
|
||||
flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'It should build an update_field action for standard field',
|
||||
context: {
|
||||
input: {
|
||||
fromFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
toFlatObjectMetadataMaps:
|
||||
replaceFlatFieldMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
flatFieldMetadata: getFlatFieldMetadataMock({
|
||||
...OPPORTUNITY_FLAT_FIELDS_MOCK.stage,
|
||||
standardOverrides: {
|
||||
description: 'new description',
|
||||
icon: 'new icon',
|
||||
},
|
||||
}),
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'It should build an update_field action for custom fields',
|
||||
context: {
|
||||
input: {
|
||||
fromFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
toFlatObjectMetadataMaps:
|
||||
replaceFlatFieldMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
flatFieldMetadata: getFlatFieldMetadataMock({
|
||||
...PET_FLAT_FIELDS_MOCK.species,
|
||||
description: 'new description',
|
||||
icon: 'new icon',
|
||||
}),
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'It should build a delete_field action',
|
||||
context: {
|
||||
input: {
|
||||
fromFlatObjectMetadataMaps:
|
||||
replaceFlatFieldMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatFieldMetadata: getFlatFieldMetadataMock({
|
||||
...PET_FLAT_FIELDS_MOCK.species,
|
||||
isActive: false,
|
||||
}),
|
||||
flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
}),
|
||||
toFlatObjectMetadataMaps: deleteFieldFromFlatObjectMetadataMapsOrThrow({
|
||||
fieldMetadataId: PET_FLAT_FIELDS_MOCK.species.id,
|
||||
objectMetadataId: PET_FLAT_FIELDS_MOCK.species.objectMetadataId,
|
||||
flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const WORKSPACE_MIGRATION_FIELD_BUILDER_TEST_CASES: WorkspaceMigrationBuilderTestCase[] =
|
||||
[
|
||||
...relationTestCases,
|
||||
...basicCrudTestCases,
|
||||
{
|
||||
title:
|
||||
'It should not infer any actions as from and to fields are identical',
|
||||
context: {
|
||||
input: {
|
||||
fromFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
toFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
-150
@@ -1,150 +0,0 @@
|
||||
import { FLAT_OBJECT_METADATA_MAPS_MOCKS } from 'src/engine/metadata-modules/flat-object-metadata-maps/mocks/flat-object-metadata-maps.mock';
|
||||
import { deleteObjectFromFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/delete-object-from-flat-object-metadata-maps-or-throw.util';
|
||||
import { replaceFlatObjectMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/replace-flat-object-metadata-in-flat-object-metadata-maps-or-throw.util';
|
||||
import { COMPANY_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/company-flat-object.mock';
|
||||
import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/get-flat-object-metadata.mock';
|
||||
import { OPPORTUNITY_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/opportunity-flat-object.mock';
|
||||
import { PET_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/pet-flat-object.mock';
|
||||
import { ROCKET_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/rocket-flat-object.mock';
|
||||
import { STANDARD_RELATION_TARGET_FLAT_OBJECT_METADATA_MOCKS } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/standard-relation-target-flat-object-metadata.mocks';
|
||||
import { fromFlatObjectMetadatasToFlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-flat-object-metadatas-to-flat-object-metadata-maps.util';
|
||||
import { type WorkspaceMigrationBuilderTestCase } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/types/workspace-migration-builder-test-case.type';
|
||||
|
||||
// TODO prastoin implement validation exception tests cases coverage too
|
||||
const DELETE_OBJECT_TEST_CASES: WorkspaceMigrationBuilderTestCase[] = [
|
||||
{
|
||||
title:
|
||||
'It should build a delete_object action with custom deactivated object',
|
||||
context: {
|
||||
input: {
|
||||
fromFlatObjectMetadataMaps:
|
||||
replaceFlatObjectMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadata: getFlatObjectMetadataMock({
|
||||
...ROCKET_FLAT_OBJECT_MOCK,
|
||||
isActive: false,
|
||||
}),
|
||||
flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
}),
|
||||
toFlatObjectMetadataMaps: deleteObjectFromFlatObjectMetadataMapsOrThrow(
|
||||
{
|
||||
flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
objectMetadataId: ROCKET_FLAT_OBJECT_MOCK.id,
|
||||
},
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title:
|
||||
'It should build a delete_object action with standard activated object',
|
||||
context: {
|
||||
input: {
|
||||
buildOptions: {
|
||||
inferDeletionFromMissingEntities: true,
|
||||
isSystemBuild: true,
|
||||
},
|
||||
fromFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
toFlatObjectMetadataMaps: deleteObjectFromFlatObjectMetadataMapsOrThrow(
|
||||
{
|
||||
flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
objectMetadataId: COMPANY_FLAT_OBJECT_MOCK.id,
|
||||
},
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const CREATE_OBJECT_TEST_CASES: WorkspaceMigrationBuilderTestCase[] = [
|
||||
{
|
||||
title: 'It should build a create_object action with custom object',
|
||||
context: {
|
||||
input: {
|
||||
fromFlatObjectMetadataMaps:
|
||||
fromFlatObjectMetadatasToFlatObjectMetadataMaps([
|
||||
PET_FLAT_OBJECT_MOCK,
|
||||
...STANDARD_RELATION_TARGET_FLAT_OBJECT_METADATA_MOCKS,
|
||||
]),
|
||||
toFlatObjectMetadataMaps:
|
||||
fromFlatObjectMetadatasToFlatObjectMetadataMaps([
|
||||
PET_FLAT_OBJECT_MOCK,
|
||||
ROCKET_FLAT_OBJECT_MOCK,
|
||||
...STANDARD_RELATION_TARGET_FLAT_OBJECT_METADATA_MOCKS,
|
||||
]),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'It should build a create_object action with standard object',
|
||||
context: {
|
||||
input: {
|
||||
fromFlatObjectMetadataMaps:
|
||||
fromFlatObjectMetadatasToFlatObjectMetadataMaps([
|
||||
ROCKET_FLAT_OBJECT_MOCK,
|
||||
...STANDARD_RELATION_TARGET_FLAT_OBJECT_METADATA_MOCKS,
|
||||
]),
|
||||
toFlatObjectMetadataMaps:
|
||||
fromFlatObjectMetadatasToFlatObjectMetadataMaps([
|
||||
PET_FLAT_OBJECT_MOCK,
|
||||
ROCKET_FLAT_OBJECT_MOCK,
|
||||
...STANDARD_RELATION_TARGET_FLAT_OBJECT_METADATA_MOCKS,
|
||||
]),
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const UPDATE_OBJECT_TEST_CASES: WorkspaceMigrationBuilderTestCase[] = [
|
||||
{
|
||||
title: 'It should build an update_object for custom object',
|
||||
context: {
|
||||
input: {
|
||||
fromFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
toFlatObjectMetadataMaps:
|
||||
replaceFlatObjectMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
flatObjectMetadata: getFlatObjectMetadataMock({
|
||||
...PET_FLAT_OBJECT_MOCK,
|
||||
nameSingular: 'prastouin',
|
||||
namePlural: 'prastoins',
|
||||
isLabelSyncedWithName: false,
|
||||
}),
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'It should build an update_object for standard object',
|
||||
context: {
|
||||
input: {
|
||||
fromFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
toFlatObjectMetadataMaps:
|
||||
replaceFlatObjectMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
flatObjectMetadata: getFlatObjectMetadataMock({
|
||||
...OPPORTUNITY_FLAT_OBJECT_MOCK,
|
||||
standardOverrides: {
|
||||
description: 'Updated description',
|
||||
},
|
||||
}),
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const WORKSPACE_MIGRATION_OBJECT_BUILDER_TEST_CASES: WorkspaceMigrationBuilderTestCase[] =
|
||||
[
|
||||
...DELETE_OBJECT_TEST_CASES,
|
||||
...CREATE_OBJECT_TEST_CASES,
|
||||
...UPDATE_OBJECT_TEST_CASES,
|
||||
{
|
||||
title: 'It should not infer any actions as from and to are identical',
|
||||
context: {
|
||||
input: {
|
||||
fromFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
toFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
import { type EachTestingContext } from 'twenty-shared/testing';
|
||||
|
||||
import { type WorkspaceMigrationBuildArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/services/workspace-migration-builder-v2.service';
|
||||
import { type ConvertActionTypeToCamelCase } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/convert-action-type-to-camel-case.type';
|
||||
import { type WorkspaceMigrationActionTypeV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-action-common-v2';
|
||||
|
||||
export type CamelCasedWorkspaceMigrationActionsType =
|
||||
ConvertActionTypeToCamelCase<WorkspaceMigrationActionTypeV2>;
|
||||
|
||||
export type ExpectedActionCounters = Partial<
|
||||
Record<CamelCasedWorkspaceMigrationActionsType, number>
|
||||
>;
|
||||
|
||||
type TestWorkspaceMigrationBuildArgs = Omit<
|
||||
WorkspaceMigrationBuildArgs,
|
||||
'workspaceId' | 'buildOptions'
|
||||
> &
|
||||
Partial<Pick<WorkspaceMigrationBuildArgs, 'buildOptions'>>;
|
||||
|
||||
export type WorkspaceMigrationBuilderTestCase = EachTestingContext<{
|
||||
input:
|
||||
| TestWorkspaceMigrationBuildArgs
|
||||
| (() => TestWorkspaceMigrationBuildArgs);
|
||||
}>;
|
||||
-105
@@ -1,105 +0,0 @@
|
||||
import { extractRecordIdsAndDatesAsExpectAny } from 'test/utils/extract-record-ids-and-dates-as-expect-any';
|
||||
import { eachTestingContextFilter } from 'twenty-shared/testing';
|
||||
|
||||
import { type FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
||||
import { FlatFieldMetadataTypeValidatorService } from 'src/engine/metadata-modules/flat-field-metadata/services/flat-field-metadata-type-validator.service';
|
||||
import { FlatFieldMetadataValidatorService } from 'src/engine/metadata-modules/flat-field-metadata/services/flat-field-metadata-validator.service';
|
||||
import { FlatObjectMetadataValidatorService } from 'src/engine/metadata-modules/flat-object-metadata/services/flat-object-metadata-validator.service';
|
||||
import { WORKSPACE_MIGRATION_FIELD_BUILDER_TEST_CASES } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/common/workspace-migration-builder-field-test-case';
|
||||
import { WORKSPACE_MIGRATION_OBJECT_BUILDER_TEST_CASES } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/common/workspace-migration-builder-object-test-case';
|
||||
import { type WorkspaceMigrationBuilderTestCase } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/types/workspace-migration-builder-test-case.type';
|
||||
import { WorkspaceMigrationV2FieldActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/field/services/workspace-migration-v2-field-actions-builder.service';
|
||||
import { WorkspaceMigrationV2ObjectActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/object/services/workspace-migration-v2-object-actions-builder.service';
|
||||
import { WorkspaceMigrationBuilderV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/services/workspace-migration-builder-v2.service';
|
||||
``;
|
||||
|
||||
const allWorkspaceBuilderTestCases: {
|
||||
label: string;
|
||||
testCases: WorkspaceMigrationBuilderTestCase[];
|
||||
}[] = [
|
||||
{
|
||||
label: 'object',
|
||||
testCases: WORKSPACE_MIGRATION_OBJECT_BUILDER_TEST_CASES,
|
||||
},
|
||||
{
|
||||
label: 'field',
|
||||
testCases: WORKSPACE_MIGRATION_FIELD_BUILDER_TEST_CASES,
|
||||
},
|
||||
];
|
||||
|
||||
describe.each(allWorkspaceBuilderTestCases)(
|
||||
'Workspace migration builder $label actions test suite',
|
||||
({ testCases }) => {
|
||||
let service: WorkspaceMigrationBuilderV2Service;
|
||||
let featureFlagService: FeatureFlagService;
|
||||
let flatFieldMetadataTypeValidatorService: FlatFieldMetadataTypeValidatorService;
|
||||
let flatFieldMetadataValidatorService: FlatFieldMetadataValidatorService;
|
||||
let flatObjectMetadataValidatorService: FlatObjectMetadataValidatorService;
|
||||
let objectActionsBuilder: WorkspaceMigrationV2ObjectActionsBuilderService;
|
||||
let fieldActionsBuilder: WorkspaceMigrationV2FieldActionsBuilderService;
|
||||
|
||||
beforeEach(() => {
|
||||
featureFlagService = {
|
||||
isFeatureEnabled: jest.fn().mockResolvedValue(true),
|
||||
} as any;
|
||||
|
||||
flatFieldMetadataTypeValidatorService =
|
||||
new FlatFieldMetadataTypeValidatorService(featureFlagService);
|
||||
|
||||
flatFieldMetadataValidatorService = new FlatFieldMetadataValidatorService(
|
||||
flatFieldMetadataTypeValidatorService,
|
||||
);
|
||||
|
||||
flatObjectMetadataValidatorService =
|
||||
new FlatObjectMetadataValidatorService(
|
||||
flatFieldMetadataValidatorService,
|
||||
);
|
||||
|
||||
objectActionsBuilder =
|
||||
new WorkspaceMigrationV2ObjectActionsBuilderService(
|
||||
flatObjectMetadataValidatorService,
|
||||
);
|
||||
fieldActionsBuilder = new WorkspaceMigrationV2FieldActionsBuilderService(
|
||||
flatFieldMetadataValidatorService,
|
||||
);
|
||||
service = new WorkspaceMigrationBuilderV2Service(
|
||||
objectActionsBuilder,
|
||||
fieldActionsBuilder,
|
||||
);
|
||||
});
|
||||
|
||||
it.each(eachTestingContextFilter(testCases))(
|
||||
'$title',
|
||||
async ({ context: { input } }) => {
|
||||
const {
|
||||
fromFlatObjectMetadataMaps,
|
||||
toFlatObjectMetadataMaps,
|
||||
buildOptions = {
|
||||
inferDeletionFromMissingEntities: true,
|
||||
isSystemBuild: false,
|
||||
},
|
||||
} = typeof input === 'function' ? input() : input;
|
||||
const validateAndBuildResult = await service.validateAndBuild({
|
||||
buildOptions,
|
||||
fromFlatObjectMetadataMaps,
|
||||
toFlatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
expect(validateAndBuildResult.status).toBe('success');
|
||||
if (validateAndBuildResult.status !== 'success') {
|
||||
throw new Error('Should never occur');
|
||||
}
|
||||
expect(validateAndBuildResult.fieldsActions).toMatchSnapshot(
|
||||
extractRecordIdsAndDatesAsExpectAny(
|
||||
validateAndBuildResult.fieldsActions,
|
||||
),
|
||||
);
|
||||
expect(validateAndBuildResult.objectActions).toMatchSnapshot(
|
||||
extractRecordIdsAndDatesAsExpectAny(
|
||||
validateAndBuildResult.objectActions,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
+13
-24
@@ -12,31 +12,26 @@ import {
|
||||
UpdateCronTriggerAction,
|
||||
WorkspaceMigrationCronTriggerActionV2,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-cron-trigger-action-v2.type';
|
||||
import {
|
||||
CronTriggerRelatedFlatEntityMaps,
|
||||
FlatCronTriggerValidatorService,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-cron-trigger-validator.service';
|
||||
import { FlatCronTriggerValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-cron-trigger-validator.service';
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceMigrationV2CronTriggerActionsBuilderService extends WorkspaceEntityMigrationBuilderV2Service<
|
||||
'cronTrigger',
|
||||
FlatCronTrigger,
|
||||
WorkspaceMigrationCronTriggerActionV2,
|
||||
CronTriggerRelatedFlatEntityMaps
|
||||
undefined
|
||||
> {
|
||||
constructor(
|
||||
private readonly flatCronTriggerValidatorService: FlatCronTriggerValidatorService,
|
||||
) {
|
||||
super();
|
||||
super('cronTrigger');
|
||||
}
|
||||
|
||||
protected async validateFlatEntityCreation({
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
flatEntityToValidate: flatCronTriggerToValidate,
|
||||
optimisticFlatEntityMaps: optimisticFlatCronTriggerMaps,
|
||||
}: FlatEntityValidationArgs<
|
||||
FlatCronTrigger,
|
||||
CronTriggerRelatedFlatEntityMaps
|
||||
>): Promise<
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
}: FlatEntityValidationArgs<FlatCronTrigger>): Promise<
|
||||
FlatEntityValidationReturnType<
|
||||
WorkspaceMigrationCronTriggerActionV2,
|
||||
FlatCronTrigger
|
||||
@@ -47,7 +42,6 @@ export class WorkspaceMigrationV2CronTriggerActionsBuilderService extends Worksp
|
||||
{
|
||||
flatCronTriggerToValidate,
|
||||
optimisticFlatCronTriggerMaps,
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -64,17 +58,15 @@ export class WorkspaceMigrationV2CronTriggerActionsBuilderService extends Worksp
|
||||
type: 'create_cron_trigger',
|
||||
cronTrigger: flatCronTriggerToValidate,
|
||||
},
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
};
|
||||
}
|
||||
|
||||
protected async validateFlatEntityDeletion({
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
flatEntityToValidate: flatCronTriggerToValidate,
|
||||
optimisticFlatEntityMaps: optimisticFlatCronTriggerMaps,
|
||||
}: FlatEntityValidationArgs<
|
||||
FlatCronTrigger,
|
||||
CronTriggerRelatedFlatEntityMaps
|
||||
>): Promise<
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
}: FlatEntityValidationArgs<FlatCronTrigger>): Promise<
|
||||
FlatEntityValidationReturnType<
|
||||
WorkspaceMigrationCronTriggerActionV2,
|
||||
FlatCronTrigger
|
||||
@@ -84,7 +76,6 @@ export class WorkspaceMigrationV2CronTriggerActionsBuilderService extends Worksp
|
||||
this.flatCronTriggerValidatorService.validateFlatCronTriggerDeletion({
|
||||
flatCronTriggerToValidate,
|
||||
optimisticFlatCronTriggerMaps,
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
});
|
||||
|
||||
if (validationResult.errors.length > 0) {
|
||||
@@ -100,17 +91,15 @@ export class WorkspaceMigrationV2CronTriggerActionsBuilderService extends Worksp
|
||||
type: 'delete_cron_trigger',
|
||||
cronTriggerId: flatCronTriggerToValidate.id,
|
||||
},
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
};
|
||||
}
|
||||
|
||||
protected async validateFlatEntityUpdate({
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
flatEntityUpdate: { from: fromFlatCronTrigger, to: toFlatCronTrigger },
|
||||
optimisticFlatEntityMaps: optimisticFlatCronTriggerMaps,
|
||||
}: FlatEntityUpdateValidationArgs<
|
||||
FlatCronTrigger,
|
||||
CronTriggerRelatedFlatEntityMaps
|
||||
>): Promise<
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
}: FlatEntityUpdateValidationArgs<FlatCronTrigger>): Promise<
|
||||
| FlatEntityValidationReturnType<
|
||||
WorkspaceMigrationCronTriggerActionV2,
|
||||
FlatCronTrigger
|
||||
@@ -130,7 +119,6 @@ export class WorkspaceMigrationV2CronTriggerActionsBuilderService extends Worksp
|
||||
this.flatCronTriggerValidatorService.validateFlatCronTriggerUpdate({
|
||||
flatCronTriggerToValidate: toFlatCronTrigger,
|
||||
optimisticFlatCronTriggerMaps,
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
});
|
||||
|
||||
if (validationResult.errors.length > 0) {
|
||||
@@ -149,6 +137,7 @@ export class WorkspaceMigrationV2CronTriggerActionsBuilderService extends Worksp
|
||||
return {
|
||||
status: 'success',
|
||||
action: updateCronTriggerAction,
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+13
-24
@@ -12,31 +12,26 @@ import {
|
||||
UpdateDatabaseEventTriggerAction,
|
||||
WorkspaceMigrationDatabaseEventTriggerActionV2,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-database-event-trigger-action-v2.type';
|
||||
import {
|
||||
DatabaseEventTriggerRelatedFlatEntityMaps,
|
||||
FlatDatabaseEventTriggerValidatorService,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-database-event-trigger-validator.service';
|
||||
import { FlatDatabaseEventTriggerValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-database-event-trigger-validator.service';
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceMigrationV2DatabaseEventTriggerActionsBuilderService extends WorkspaceEntityMigrationBuilderV2Service<
|
||||
'databaseEventTrigger',
|
||||
FlatDatabaseEventTrigger,
|
||||
WorkspaceMigrationDatabaseEventTriggerActionV2,
|
||||
DatabaseEventTriggerRelatedFlatEntityMaps
|
||||
undefined
|
||||
> {
|
||||
constructor(
|
||||
private readonly flatDatabaseEventTriggerValidatorService: FlatDatabaseEventTriggerValidatorService,
|
||||
) {
|
||||
super();
|
||||
super('databaseEventTrigger');
|
||||
}
|
||||
|
||||
protected async validateFlatEntityCreation({
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
flatEntityToValidate: flatDatabaseEventTriggerToValidate,
|
||||
optimisticFlatEntityMaps: optimisticFlatDatabaseEventTriggerMaps,
|
||||
}: FlatEntityValidationArgs<
|
||||
FlatDatabaseEventTrigger,
|
||||
DatabaseEventTriggerRelatedFlatEntityMaps
|
||||
>): Promise<
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
}: FlatEntityValidationArgs<FlatDatabaseEventTrigger>): Promise<
|
||||
FlatEntityValidationReturnType<
|
||||
WorkspaceMigrationDatabaseEventTriggerActionV2,
|
||||
FlatDatabaseEventTrigger
|
||||
@@ -47,7 +42,6 @@ export class WorkspaceMigrationV2DatabaseEventTriggerActionsBuilderService exten
|
||||
{
|
||||
flatDatabaseEventTriggerToValidate,
|
||||
optimisticFlatDatabaseEventTriggerMaps,
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -64,17 +58,15 @@ export class WorkspaceMigrationV2DatabaseEventTriggerActionsBuilderService exten
|
||||
type: 'create_database_event_trigger',
|
||||
databaseEventTrigger: flatDatabaseEventTriggerToValidate,
|
||||
},
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
};
|
||||
}
|
||||
|
||||
protected async validateFlatEntityDeletion({
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
flatEntityToValidate: flatDatabaseEventTriggerToValidate,
|
||||
optimisticFlatEntityMaps: optimisticFlatDatabaseEventTriggerMaps,
|
||||
}: FlatEntityValidationArgs<
|
||||
FlatDatabaseEventTrigger,
|
||||
DatabaseEventTriggerRelatedFlatEntityMaps
|
||||
>): Promise<
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
}: FlatEntityValidationArgs<FlatDatabaseEventTrigger>): Promise<
|
||||
FlatEntityValidationReturnType<
|
||||
WorkspaceMigrationDatabaseEventTriggerActionV2,
|
||||
FlatDatabaseEventTrigger
|
||||
@@ -85,7 +77,6 @@ export class WorkspaceMigrationV2DatabaseEventTriggerActionsBuilderService exten
|
||||
{
|
||||
flatDatabaseEventTriggerToValidate,
|
||||
optimisticFlatDatabaseEventTriggerMaps,
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -102,20 +93,18 @@ export class WorkspaceMigrationV2DatabaseEventTriggerActionsBuilderService exten
|
||||
type: 'delete_database_event_trigger',
|
||||
databaseEventTriggerId: flatDatabaseEventTriggerToValidate.id,
|
||||
},
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
};
|
||||
}
|
||||
|
||||
protected async validateFlatEntityUpdate({
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
flatEntityUpdate: {
|
||||
from: fromFlatDatabaseEventTrigger,
|
||||
to: toFlatDatabaseEventTrigger,
|
||||
},
|
||||
optimisticFlatEntityMaps: optimisticFlatDatabaseEventTriggerMaps,
|
||||
}: FlatEntityUpdateValidationArgs<
|
||||
FlatDatabaseEventTrigger,
|
||||
DatabaseEventTriggerRelatedFlatEntityMaps
|
||||
>): Promise<
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
}: FlatEntityUpdateValidationArgs<FlatDatabaseEventTrigger>): Promise<
|
||||
| FlatEntityValidationReturnType<
|
||||
WorkspaceMigrationDatabaseEventTriggerActionV2,
|
||||
FlatDatabaseEventTrigger
|
||||
@@ -137,7 +126,6 @@ export class WorkspaceMigrationV2DatabaseEventTriggerActionsBuilderService exten
|
||||
{
|
||||
flatDatabaseEventTriggerToValidate: toFlatDatabaseEventTrigger,
|
||||
optimisticFlatDatabaseEventTriggerMaps,
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -157,6 +145,7 @@ export class WorkspaceMigrationV2DatabaseEventTriggerActionsBuilderService exten
|
||||
return {
|
||||
status: 'success',
|
||||
action: updateDatabaseEventTriggerAction,
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+199
-237
@@ -1,255 +1,217 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { FromTo } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { FlatFieldMetadataValidatorService } from 'src/engine/metadata-modules/flat-field-metadata/services/flat-field-metadata-validator.service';
|
||||
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/core-modules/common/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
|
||||
import { replaceFlatEntityInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/replace-flat-entity-in-flat-entity-maps-or-throw.util';
|
||||
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { compareTwoFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/compare-two-flat-field-metadata.util';
|
||||
import { isMorphOrRelationFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-metadata.util';
|
||||
import { FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
import { addFlatFieldMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/add-flat-field-metadata-in-flat-object-metadata-maps-or-throw.util';
|
||||
import { deleteFieldFromFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/delete-field-from-flat-object-metadata-maps-or-throw.util';
|
||||
import { replaceFlatFieldMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/replace-flat-field-metadata-in-flat-object-metadata-maps-or-throw.util';
|
||||
import { computeMorphOrRelationTargetFlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata/utils/compute-morph-or-relation-target-flat-object-metadata-maps.util';
|
||||
import { WorkspaceMigrationV2BuilderOptions } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/services/workspace-migration-builder-v2.service';
|
||||
import { ValidateAndBuildMetadataResult } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/validate-and-build-metadata-result.type';
|
||||
import { type WorkspaceMigrationFieldActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-field-action-v2';
|
||||
import { type UpdatedObjectMetadataDeletedCreatedUpdatedFieldMatrix } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/compute-updated-object-metadata-deleted-created-updated-field-matrix.util';
|
||||
import {
|
||||
getWorkspaceMigrationV2FieldCreateAction,
|
||||
getWorkspaceMigrationV2FieldDeleteAction,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/get-workspace-migration-v2-field-actions';
|
||||
FlatEntityUpdateValidationArgs,
|
||||
FlatEntityValidationArgs,
|
||||
FlatEntityValidationReturnType,
|
||||
WorkspaceEntityMigrationBuilderV2Service,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/services/workspace-entity-migration-builder-v2.service';
|
||||
import {
|
||||
UpdateFieldAction,
|
||||
WorkspaceMigrationFieldActionV2,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-field-action-v2';
|
||||
import { FlatFieldMetadataValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-field-metadata-validator.service';
|
||||
|
||||
type BuildWorkspaceMigrationV2FieldActionsArgs = FromTo<
|
||||
FlatObjectMetadataMaps,
|
||||
export type FieldMetadataRelatedFlatEntityMaps = Pick<
|
||||
AllFlatEntityMaps,
|
||||
'flatObjectMetadataMaps'
|
||||
> & {
|
||||
buildOptions: WorkspaceMigrationV2BuilderOptions;
|
||||
objectMetadataDeletedCreatedUpdatedFields: UpdatedObjectMetadataDeletedCreatedUpdatedFieldMatrix[];
|
||||
};
|
||||
|
||||
type ValidateFlatObjectMetadataFlatFieldMetadataMatrixArgs = FromTo<
|
||||
FlatObjectMetadataMaps,
|
||||
'flatObjectMetadataMaps'
|
||||
> &
|
||||
UpdatedObjectMetadataDeletedCreatedUpdatedFieldMatrix & {
|
||||
buildOptions: WorkspaceMigrationV2BuilderOptions;
|
||||
};
|
||||
|
||||
>;
|
||||
@Injectable()
|
||||
export class WorkspaceMigrationV2FieldActionsBuilderService {
|
||||
export class WorkspaceMigrationV2FieldActionsBuilderService extends WorkspaceEntityMigrationBuilderV2Service<
|
||||
'fieldMetadata',
|
||||
FlatFieldMetadata,
|
||||
WorkspaceMigrationFieldActionV2,
|
||||
FieldMetadataRelatedFlatEntityMaps
|
||||
> {
|
||||
constructor(
|
||||
private readonly flatFieldMetadataValidatorService: FlatFieldMetadataValidatorService,
|
||||
) {}
|
||||
|
||||
private async validateFlatObjectMetadataFlatFieldMetadataMatrix({
|
||||
createdFlatFieldMetadatas,
|
||||
deletedFlatFieldMetadatas,
|
||||
flatObjectMetadata,
|
||||
updatedFlatFieldMetadatas,
|
||||
fromFlatObjectMetadataMaps,
|
||||
toFlatObjectMetadataMaps,
|
||||
buildOptions,
|
||||
}: ValidateFlatObjectMetadataFlatFieldMetadataMatrixArgs): Promise<
|
||||
ValidateAndBuildMetadataResult<WorkspaceMigrationFieldActionV2>
|
||||
> {
|
||||
const validateAndBuildResult: ValidateAndBuildMetadataResult<WorkspaceMigrationFieldActionV2> =
|
||||
{
|
||||
failed: [],
|
||||
created: [],
|
||||
deleted: [],
|
||||
updated: [],
|
||||
optimisticFlatObjectMetadataMaps: structuredClone(
|
||||
fromFlatObjectMetadataMaps,
|
||||
),
|
||||
};
|
||||
|
||||
for (const {
|
||||
from: fromFlatFieldMetadata,
|
||||
to: toFlatFieldMetadata,
|
||||
} of updatedFlatFieldMetadatas) {
|
||||
const updates = compareTwoFlatFieldMetadata({
|
||||
fromFlatFieldMetadata,
|
||||
toFlatFieldMetadata,
|
||||
});
|
||||
|
||||
if (updates.length === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const {
|
||||
workspaceId,
|
||||
id: fieldMetadataId,
|
||||
objectMetadataId,
|
||||
} = toFlatFieldMetadata;
|
||||
|
||||
const validationErrors =
|
||||
await this.flatFieldMetadataValidatorService.validateFlatFieldMetadataUpdate(
|
||||
{
|
||||
existingFlatObjectMetadataMaps:
|
||||
validateAndBuildResult.optimisticFlatObjectMetadataMaps,
|
||||
flatFieldMetadataToValidate: toFlatFieldMetadata,
|
||||
workspaceId,
|
||||
otherFlatObjectMetadataMapsToValidate: undefined,
|
||||
},
|
||||
);
|
||||
|
||||
if (validationErrors.errors.length > 0) {
|
||||
validateAndBuildResult.failed.push(validationErrors);
|
||||
continue;
|
||||
}
|
||||
|
||||
validateAndBuildResult.optimisticFlatObjectMetadataMaps =
|
||||
replaceFlatFieldMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatFieldMetadata: toFlatFieldMetadata,
|
||||
flatObjectMetadataMaps:
|
||||
validateAndBuildResult.optimisticFlatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
validateAndBuildResult.updated.push({
|
||||
type: 'update_field',
|
||||
fieldMetadataId,
|
||||
objectMetadataId,
|
||||
workspaceId,
|
||||
updates,
|
||||
});
|
||||
}
|
||||
|
||||
for (const flatFieldMetadataToCreate of createdFlatFieldMetadatas) {
|
||||
const relationTargetFlatObjectMetadataMaps =
|
||||
isMorphOrRelationFlatFieldMetadata(flatFieldMetadataToCreate)
|
||||
? computeMorphOrRelationTargetFlatObjectMetadataMaps({
|
||||
flatFieldMetadata: flatFieldMetadataToCreate,
|
||||
flatObjectMetadataMaps: toFlatObjectMetadataMaps,
|
||||
})
|
||||
: undefined;
|
||||
|
||||
const validationErrors =
|
||||
await this.flatFieldMetadataValidatorService.validateFlatFieldMetadataCreation(
|
||||
{
|
||||
existingFlatObjectMetadataMaps:
|
||||
validateAndBuildResult.optimisticFlatObjectMetadataMaps,
|
||||
flatFieldMetadataToValidate: flatFieldMetadataToCreate,
|
||||
workspaceId: flatFieldMetadataToCreate.workspaceId,
|
||||
otherFlatObjectMetadataMapsToValidate:
|
||||
relationTargetFlatObjectMetadataMaps,
|
||||
},
|
||||
);
|
||||
|
||||
if (validationErrors.errors.length > 0) {
|
||||
validateAndBuildResult.failed.push(validationErrors);
|
||||
continue;
|
||||
}
|
||||
|
||||
validateAndBuildResult.optimisticFlatObjectMetadataMaps =
|
||||
addFlatFieldMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatFieldMetadata: flatFieldMetadataToCreate,
|
||||
flatObjectMetadataMaps:
|
||||
validateAndBuildResult.optimisticFlatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
const createFieldAction = getWorkspaceMigrationV2FieldCreateAction({
|
||||
flatFieldMetadata: flatFieldMetadataToCreate,
|
||||
flatObjectMetadata,
|
||||
});
|
||||
|
||||
validateAndBuildResult.created.push(createFieldAction);
|
||||
}
|
||||
|
||||
for (const flatFieldMetadataToDelete of buildOptions.inferDeletionFromMissingEntities
|
||||
? deletedFlatFieldMetadatas
|
||||
: []) {
|
||||
const validationErrors =
|
||||
this.flatFieldMetadataValidatorService.validateFlatFieldMetadataDeletion(
|
||||
{
|
||||
existingFlatObjectMetadataMaps:
|
||||
validateAndBuildResult.optimisticFlatObjectMetadataMaps,
|
||||
flatFieldMetadataToDelete,
|
||||
},
|
||||
);
|
||||
|
||||
if (validationErrors.errors.length > 0) {
|
||||
validateAndBuildResult.failed.push(validationErrors);
|
||||
continue;
|
||||
}
|
||||
|
||||
validateAndBuildResult.optimisticFlatObjectMetadataMaps =
|
||||
deleteFieldFromFlatObjectMetadataMapsOrThrow({
|
||||
fieldMetadataId: flatFieldMetadataToDelete.id,
|
||||
flatObjectMetadataMaps:
|
||||
validateAndBuildResult.optimisticFlatObjectMetadataMaps,
|
||||
objectMetadataId: flatFieldMetadataToDelete.objectMetadataId,
|
||||
});
|
||||
|
||||
const deleteFieldAction = getWorkspaceMigrationV2FieldDeleteAction({
|
||||
flatFieldMetadata: flatFieldMetadataToDelete,
|
||||
flatObjectMetadata,
|
||||
});
|
||||
|
||||
validateAndBuildResult.deleted.push(deleteFieldAction);
|
||||
}
|
||||
|
||||
return validateAndBuildResult;
|
||||
private readonly flatFieldValidatorService: FlatFieldMetadataValidatorService,
|
||||
) {
|
||||
super('fieldMetadata');
|
||||
}
|
||||
|
||||
public async validateAndBuildFieldActions({
|
||||
buildOptions,
|
||||
fromFlatObjectMetadataMaps,
|
||||
toFlatObjectMetadataMaps,
|
||||
objectMetadataDeletedCreatedUpdatedFields,
|
||||
}: BuildWorkspaceMigrationV2FieldActionsArgs): Promise<
|
||||
ValidateAndBuildMetadataResult<WorkspaceMigrationFieldActionV2>
|
||||
protected async validateFlatEntityCreation({
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
flatEntityToValidate: flatFieldMetadataToValidate,
|
||||
optimisticFlatEntityMaps: optimisticFlatFieldMetadataMaps,
|
||||
workspaceId,
|
||||
remainingFlatEntityMapsToValidate,
|
||||
}: FlatEntityValidationArgs<
|
||||
FlatFieldMetadata,
|
||||
FieldMetadataRelatedFlatEntityMaps
|
||||
>): Promise<
|
||||
FlatEntityValidationReturnType<
|
||||
WorkspaceMigrationFieldActionV2,
|
||||
FlatFieldMetadata,
|
||||
FieldMetadataRelatedFlatEntityMaps
|
||||
>
|
||||
> {
|
||||
let validateAndBuildResult: ValidateAndBuildMetadataResult<WorkspaceMigrationFieldActionV2> =
|
||||
{
|
||||
failed: [],
|
||||
created: [],
|
||||
deleted: [],
|
||||
updated: [],
|
||||
optimisticFlatObjectMetadataMaps: structuredClone(
|
||||
fromFlatObjectMetadataMaps,
|
||||
),
|
||||
};
|
||||
const validationResult =
|
||||
await this.flatFieldValidatorService.validateFlatFieldMetadataCreation({
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
flatFieldMetadataToValidate,
|
||||
optimisticFlatFieldMetadataMaps,
|
||||
workspaceId,
|
||||
remainingFlatEntityMapsToValidate,
|
||||
});
|
||||
|
||||
for (const {
|
||||
createdFlatFieldMetadatas,
|
||||
deletedFlatFieldMetadatas,
|
||||
updatedFlatFieldMetadatas,
|
||||
flatObjectMetadata,
|
||||
} of objectMetadataDeletedCreatedUpdatedFields) {
|
||||
const flatObjectMetadataFlatFieldMatrixValidateAndBuildResult =
|
||||
await this.validateFlatObjectMetadataFlatFieldMetadataMatrix({
|
||||
createdFlatFieldMetadatas,
|
||||
deletedFlatFieldMetadatas,
|
||||
flatObjectMetadata,
|
||||
fromFlatObjectMetadataMaps:
|
||||
validateAndBuildResult.optimisticFlatObjectMetadataMaps,
|
||||
updatedFlatFieldMetadatas,
|
||||
toFlatObjectMetadataMaps,
|
||||
buildOptions,
|
||||
});
|
||||
|
||||
validateAndBuildResult = {
|
||||
failed: [
|
||||
...validateAndBuildResult.failed,
|
||||
...flatObjectMetadataFlatFieldMatrixValidateAndBuildResult.failed,
|
||||
],
|
||||
created: [
|
||||
...validateAndBuildResult.created,
|
||||
...flatObjectMetadataFlatFieldMatrixValidateAndBuildResult.created,
|
||||
],
|
||||
deleted: [
|
||||
...validateAndBuildResult.deleted,
|
||||
...flatObjectMetadataFlatFieldMatrixValidateAndBuildResult.deleted,
|
||||
],
|
||||
updated: [
|
||||
...validateAndBuildResult.updated,
|
||||
...flatObjectMetadataFlatFieldMatrixValidateAndBuildResult.updated,
|
||||
],
|
||||
optimisticFlatObjectMetadataMaps:
|
||||
flatObjectMetadataFlatFieldMatrixValidateAndBuildResult.optimisticFlatObjectMetadataMaps,
|
||||
if (validationResult.errors.length > 0) {
|
||||
return {
|
||||
status: 'fail',
|
||||
...validationResult,
|
||||
};
|
||||
}
|
||||
|
||||
return validateAndBuildResult;
|
||||
const flatObjectMetadata = findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: flatFieldMetadataToValidate.objectMetadataId,
|
||||
flatEntityMaps: dependencyOptimisticFlatEntityMaps.flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
const updatedFlatObjectMetadataMaps =
|
||||
replaceFlatEntityInFlatEntityMapsOrThrow({
|
||||
flatEntity: {
|
||||
...flatObjectMetadata,
|
||||
fieldMetadataIds: [
|
||||
...flatObjectMetadata.fieldMetadataIds,
|
||||
flatFieldMetadataToValidate.id,
|
||||
],
|
||||
},
|
||||
flatEntityMaps:
|
||||
dependencyOptimisticFlatEntityMaps.flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
action: {
|
||||
type: 'create_field',
|
||||
objectMetadataId: flatFieldMetadataToValidate.objectMetadataId,
|
||||
flatFieldMetadatas: [flatFieldMetadataToValidate],
|
||||
},
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
flatObjectMetadataMaps: updatedFlatObjectMetadataMaps,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
protected async validateFlatEntityDeletion({
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
flatEntityToValidate: flatFieldMetadataToValidate,
|
||||
optimisticFlatEntityMaps: optimisticFlatFieldMetadataMaps,
|
||||
workspaceId,
|
||||
}: FlatEntityValidationArgs<
|
||||
FlatFieldMetadata,
|
||||
FieldMetadataRelatedFlatEntityMaps
|
||||
>): Promise<
|
||||
FlatEntityValidationReturnType<
|
||||
WorkspaceMigrationFieldActionV2,
|
||||
FlatFieldMetadata,
|
||||
FieldMetadataRelatedFlatEntityMaps
|
||||
>
|
||||
> {
|
||||
const validationResult =
|
||||
this.flatFieldValidatorService.validateFlatFieldMetadataDeletion({
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
flatFieldMetadataToValidate,
|
||||
optimisticFlatFieldMetadataMaps,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
if (validationResult.errors.length > 0) {
|
||||
return {
|
||||
status: 'fail',
|
||||
...validationResult,
|
||||
};
|
||||
}
|
||||
|
||||
const flatObjectMetadata = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: flatFieldMetadataToValidate.objectMetadataId,
|
||||
flatEntityMaps: dependencyOptimisticFlatEntityMaps.flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
const updatedFlatObjectMetadataMaps = isDefined(flatObjectMetadata)
|
||||
? replaceFlatEntityInFlatEntityMapsOrThrow({
|
||||
flatEntity: {
|
||||
...flatObjectMetadata,
|
||||
fieldMetadataIds: flatObjectMetadata.fieldMetadataIds.filter(
|
||||
(id) => id !== flatFieldMetadataToValidate.id,
|
||||
),
|
||||
},
|
||||
flatEntityMaps:
|
||||
dependencyOptimisticFlatEntityMaps.flatObjectMetadataMaps,
|
||||
})
|
||||
: dependencyOptimisticFlatEntityMaps.flatObjectMetadataMaps;
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
action: {
|
||||
type: 'delete_field',
|
||||
fieldMetadataId: flatFieldMetadataToValidate.id,
|
||||
objectMetadataId: flatFieldMetadataToValidate.objectMetadataId,
|
||||
},
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
flatObjectMetadataMaps: updatedFlatObjectMetadataMaps,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
protected async validateFlatEntityUpdate({
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
flatEntityUpdate: { from: fromFlatFieldMetadata, to: toFlatFieldMetadata },
|
||||
optimisticFlatEntityMaps: optimisticFlatFieldMetadataMaps,
|
||||
workspaceId,
|
||||
}: FlatEntityUpdateValidationArgs<
|
||||
FlatFieldMetadata,
|
||||
FieldMetadataRelatedFlatEntityMaps
|
||||
>): Promise<
|
||||
| FlatEntityValidationReturnType<
|
||||
WorkspaceMigrationFieldActionV2,
|
||||
FlatFieldMetadata,
|
||||
FieldMetadataRelatedFlatEntityMaps
|
||||
>
|
||||
| undefined
|
||||
> {
|
||||
const flatFieldMetadataUpdatedProperties = compareTwoFlatFieldMetadata({
|
||||
fromFlatFieldMetadata,
|
||||
toFlatFieldMetadata,
|
||||
});
|
||||
|
||||
if (flatFieldMetadataUpdatedProperties.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const validationResult =
|
||||
await this.flatFieldValidatorService.validateFlatFieldMetadataUpdate({
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
flatFieldMetadataToValidate: toFlatFieldMetadata,
|
||||
optimisticFlatFieldMetadataMaps,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
if (validationResult.errors.length > 0) {
|
||||
return {
|
||||
status: 'fail',
|
||||
...validationResult,
|
||||
};
|
||||
}
|
||||
|
||||
const updateFieldAction: UpdateFieldAction = {
|
||||
type: 'update_field',
|
||||
fieldMetadataId: toFlatFieldMetadata.id,
|
||||
objectMetadataId: toFlatFieldMetadata.objectMetadataId,
|
||||
updates: flatFieldMetadataUpdatedProperties,
|
||||
};
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
action: updateFieldAction,
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+59
-5
@@ -1,7 +1,12 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { deleteFlatEntityFromFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/delete-flat-entity-from-flat-entity-maps-or-throw.util';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/core-modules/common/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
|
||||
import { replaceFlatEntityInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/replace-flat-entity-in-flat-entity-maps-or-throw.util';
|
||||
import { FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type';
|
||||
import { compareTwoFlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/utils/compare-two-flat-index-metadata.util';
|
||||
import {
|
||||
@@ -15,10 +20,11 @@ import { FlatIndexValidatorService } from 'src/engine/workspace-manager/workspac
|
||||
|
||||
export type IndexRelatedFlatEntityMaps = Pick<
|
||||
AllFlatEntityMaps,
|
||||
'flatObjectMetadataMaps'
|
||||
'flatFieldMetadataMaps' | 'flatObjectMetadataMaps'
|
||||
>;
|
||||
@Injectable()
|
||||
export class WorkspaceMigrationV2IndexActionsBuilderService extends WorkspaceEntityMigrationBuilderV2Service<
|
||||
'index',
|
||||
FlatIndexMetadata,
|
||||
WorkspaceMigrationIndexActionV2,
|
||||
IndexRelatedFlatEntityMaps
|
||||
@@ -26,7 +32,7 @@ export class WorkspaceMigrationV2IndexActionsBuilderService extends WorkspaceEnt
|
||||
constructor(
|
||||
private readonly flatIndexValidatorService: FlatIndexValidatorService,
|
||||
) {
|
||||
super();
|
||||
super('index');
|
||||
}
|
||||
|
||||
protected async validateFlatEntityCreation({
|
||||
@@ -39,7 +45,8 @@ export class WorkspaceMigrationV2IndexActionsBuilderService extends WorkspaceEnt
|
||||
>): Promise<
|
||||
FlatEntityValidationReturnType<
|
||||
WorkspaceMigrationIndexActionV2,
|
||||
FlatIndexMetadata
|
||||
FlatIndexMetadata,
|
||||
IndexRelatedFlatEntityMaps
|
||||
>
|
||||
> {
|
||||
const validationResult =
|
||||
@@ -56,12 +63,34 @@ export class WorkspaceMigrationV2IndexActionsBuilderService extends WorkspaceEnt
|
||||
};
|
||||
}
|
||||
|
||||
const flatObjectMetadata = findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: flatIndexToValidate.objectMetadataId,
|
||||
flatEntityMaps: dependencyOptimisticFlatEntityMaps.flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
const updatedFlatObjectMetadataMaps =
|
||||
replaceFlatEntityInFlatEntityMapsOrThrow({
|
||||
flatEntity: {
|
||||
...flatObjectMetadata,
|
||||
indexMetadataIds: [
|
||||
...flatObjectMetadata.indexMetadataIds,
|
||||
flatIndexToValidate.id,
|
||||
],
|
||||
},
|
||||
flatEntityMaps:
|
||||
dependencyOptimisticFlatEntityMaps.flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
action: {
|
||||
type: 'create_index',
|
||||
flatIndexMetadata: flatIndexToValidate,
|
||||
},
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
...dependencyOptimisticFlatEntityMaps,
|
||||
flatObjectMetadataMaps: updatedFlatObjectMetadataMaps,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -75,7 +104,8 @@ export class WorkspaceMigrationV2IndexActionsBuilderService extends WorkspaceEnt
|
||||
>): Promise<
|
||||
FlatEntityValidationReturnType<
|
||||
WorkspaceMigrationIndexActionV2,
|
||||
FlatIndexMetadata
|
||||
FlatIndexMetadata,
|
||||
IndexRelatedFlatEntityMaps
|
||||
>
|
||||
> {
|
||||
const validationResult =
|
||||
@@ -92,12 +122,34 @@ export class WorkspaceMigrationV2IndexActionsBuilderService extends WorkspaceEnt
|
||||
};
|
||||
}
|
||||
|
||||
const flatObjectMetadata = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: flatIndexToValidate.objectMetadataId,
|
||||
flatEntityMaps: dependencyOptimisticFlatEntityMaps.flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
const updatedFlatObjectMetadataMaps = isDefined(flatObjectMetadata)
|
||||
? replaceFlatEntityInFlatEntityMapsOrThrow({
|
||||
flatEntity: {
|
||||
...flatObjectMetadata,
|
||||
indexMetadataIds: flatObjectMetadata.indexMetadataIds.filter(
|
||||
(id) => id !== flatIndexToValidate.id,
|
||||
),
|
||||
},
|
||||
flatEntityMaps:
|
||||
dependencyOptimisticFlatEntityMaps.flatObjectMetadataMaps,
|
||||
})
|
||||
: dependencyOptimisticFlatEntityMaps.flatObjectMetadataMaps;
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
action: {
|
||||
type: 'delete_index',
|
||||
flatIndexMetadataId: flatIndexToValidate.id,
|
||||
},
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
...dependencyOptimisticFlatEntityMaps,
|
||||
flatObjectMetadataMaps: updatedFlatObjectMetadataMaps,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -111,7 +163,8 @@ export class WorkspaceMigrationV2IndexActionsBuilderService extends WorkspaceEnt
|
||||
>): Promise<
|
||||
| FlatEntityValidationReturnType<
|
||||
WorkspaceMigrationIndexActionV2,
|
||||
FlatIndexMetadata
|
||||
FlatIndexMetadata,
|
||||
IndexRelatedFlatEntityMaps
|
||||
>
|
||||
| undefined
|
||||
> {
|
||||
@@ -167,6 +220,7 @@ export class WorkspaceMigrationV2IndexActionsBuilderService extends WorkspaceEnt
|
||||
flatIndexMetadata: toFlatIndex,
|
||||
},
|
||||
],
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+144
-157
@@ -1,186 +1,173 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { FromTo } from 'twenty-shared/types';
|
||||
|
||||
import { FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
import { addFlatObjectMetadataToFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/add-flat-object-metadata-to-flat-object-metadata-maps-or-throw.util';
|
||||
import { deleteObjectFromFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/delete-object-from-flat-object-metadata-maps-or-throw.util';
|
||||
import { replaceFlatObjectMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/replace-flat-object-metadata-in-flat-object-metadata-maps-or-throw.util';
|
||||
import { FlatObjectMetadataValidatorService } from 'src/engine/metadata-modules/flat-object-metadata/services/flat-object-metadata-validator.service';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { compareTwoFlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/utils/compare-two-flat-object-metadata.util';
|
||||
import { type CustomDeletedCreatedUpdatedMatrix } from 'src/engine/workspace-manager/workspace-migration-v2/utils/deleted-created-updated-matrix-dispatcher.util';
|
||||
import { WorkspaceMigrationV2BuilderOptions } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/services/workspace-migration-builder-v2.service';
|
||||
import { ValidateAndBuildMetadataResult } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/validate-and-build-metadata-result.type';
|
||||
import {
|
||||
FlatEntityUpdateValidationArgs,
|
||||
FlatEntityValidationArgs,
|
||||
FlatEntityValidationReturnType,
|
||||
WorkspaceEntityMigrationBuilderV2Service,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/services/workspace-entity-migration-builder-v2.service';
|
||||
import {
|
||||
UpdateObjectAction,
|
||||
WorkspaceMigrationObjectActionV2,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-object-action-v2';
|
||||
import { getWorkspaceMigrationV2FieldCreateAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/get-workspace-migration-v2-field-actions';
|
||||
import {
|
||||
getWorkspaceMigrationV2ObjectCreateAction,
|
||||
getWorkspaceMigrationV2ObjectDeleteAction,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/get-workspace-migration-v2-object-actions';
|
||||
import { FlatObjectMetadataValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-object-metadata-validator.service';
|
||||
// export type ObjectMetadataRelatedFlatEntityMaps = Pick<
|
||||
// AllFlatEntityMaps,
|
||||
// 'flatFieldMetadataMaps'
|
||||
// >;
|
||||
|
||||
export type CreatedDeletedUpdatedObjectMetadataInputMatrix = FromTo<
|
||||
FlatObjectMetadataMaps,
|
||||
'FlatObjectMetadataMaps'
|
||||
> &
|
||||
CustomDeletedCreatedUpdatedMatrix<
|
||||
'flatObjectMetadatas',
|
||||
FlatObjectMetadata
|
||||
> & {
|
||||
buildOptions: WorkspaceMigrationV2BuilderOptions;
|
||||
fromFlatObjectMetadataMaps: FlatObjectMetadataMaps;
|
||||
};
|
||||
// TODO find a solution in order to handle label indentifier validation
|
||||
export type ObjectMetadataRelatedFlatEntityMaps = undefined;
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceMigrationV2ObjectActionsBuilderService {
|
||||
export class WorkspaceMigrationV2ObjectActionsBuilderService extends WorkspaceEntityMigrationBuilderV2Service<
|
||||
'objectMetadata',
|
||||
FlatObjectMetadata,
|
||||
WorkspaceMigrationObjectActionV2,
|
||||
ObjectMetadataRelatedFlatEntityMaps
|
||||
> {
|
||||
constructor(
|
||||
private readonly flatObjectMetadataValidatorService: FlatObjectMetadataValidatorService,
|
||||
) {}
|
||||
private readonly flatObjectValidatorService: FlatObjectMetadataValidatorService,
|
||||
) {
|
||||
super('objectMetadata');
|
||||
}
|
||||
|
||||
public async validateAndBuildObjectActions({
|
||||
createdFlatObjectMetadatas,
|
||||
deletedFlatObjectMetadatas,
|
||||
updatedFlatObjectMetadatas,
|
||||
protected async validateFlatEntityCreation({
|
||||
flatEntityToValidate: flatObjectMetadataToValidate,
|
||||
optimisticFlatEntityMaps: optimisticFlatObjectMetadataMaps,
|
||||
buildOptions,
|
||||
fromFlatObjectMetadataMaps,
|
||||
toFlatObjectMetadataMaps,
|
||||
}: CreatedDeletedUpdatedObjectMetadataInputMatrix): Promise<
|
||||
ValidateAndBuildMetadataResult<WorkspaceMigrationObjectActionV2>
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
}: FlatEntityValidationArgs<
|
||||
FlatObjectMetadata,
|
||||
ObjectMetadataRelatedFlatEntityMaps
|
||||
>): Promise<
|
||||
FlatEntityValidationReturnType<
|
||||
WorkspaceMigrationObjectActionV2,
|
||||
FlatObjectMetadata,
|
||||
ObjectMetadataRelatedFlatEntityMaps
|
||||
>
|
||||
> {
|
||||
const validateAndBuildResult: ValidateAndBuildMetadataResult<WorkspaceMigrationObjectActionV2> =
|
||||
{
|
||||
failed: [],
|
||||
created: [],
|
||||
deleted: [],
|
||||
updated: [],
|
||||
optimisticFlatObjectMetadataMaps: structuredClone(
|
||||
fromFlatObjectMetadataMaps,
|
||||
),
|
||||
};
|
||||
|
||||
for (const flatObjectMetadataToCreate of createdFlatObjectMetadatas) {
|
||||
const validationErrors =
|
||||
await this.flatObjectMetadataValidatorService.validateFlatObjectMetadataCreation(
|
||||
{
|
||||
existingFlatObjectMetadataMaps:
|
||||
validateAndBuildResult.optimisticFlatObjectMetadataMaps,
|
||||
flatObjectMetadataToValidate: flatObjectMetadataToCreate,
|
||||
otherFlatObjectMetadataMapsToValidate: toFlatObjectMetadataMaps,
|
||||
},
|
||||
);
|
||||
|
||||
if (
|
||||
validationErrors.fieldValidationResults.length > 0 ||
|
||||
validationErrors.objectValidationResult.errors.length > 0
|
||||
) {
|
||||
validateAndBuildResult.failed.push(
|
||||
validationErrors.objectValidationResult,
|
||||
...validationErrors.fieldValidationResults,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
validateAndBuildResult.optimisticFlatObjectMetadataMaps =
|
||||
addFlatObjectMetadataToFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadata: flatObjectMetadataToCreate,
|
||||
flatObjectMetadataMaps:
|
||||
validateAndBuildResult.optimisticFlatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
const createFieldActions =
|
||||
flatObjectMetadataToCreate.flatFieldMetadatas.map((flatFieldMetadata) =>
|
||||
getWorkspaceMigrationV2FieldCreateAction({
|
||||
flatFieldMetadata,
|
||||
flatObjectMetadata: flatObjectMetadataToCreate,
|
||||
}),
|
||||
);
|
||||
|
||||
const createObjectAction = getWorkspaceMigrationV2ObjectCreateAction({
|
||||
flatObjectMetadata: flatObjectMetadataToCreate,
|
||||
createFieldActions,
|
||||
const validationResult =
|
||||
await this.flatObjectValidatorService.validateFlatObjectMetadataCreation({
|
||||
buildOptions,
|
||||
flatObjectMetadataToValidate,
|
||||
optimisticFlatObjectMetadataMaps,
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
});
|
||||
|
||||
validateAndBuildResult.created.push(createObjectAction);
|
||||
if (validationResult.errors.length > 0) {
|
||||
return {
|
||||
status: 'fail',
|
||||
...validationResult,
|
||||
};
|
||||
}
|
||||
|
||||
for (const flatObjectMetadataToDelete of buildOptions.inferDeletionFromMissingEntities
|
||||
? deletedFlatObjectMetadatas
|
||||
: []) {
|
||||
const validationErrors =
|
||||
this.flatObjectMetadataValidatorService.validateFlatObjectMetadataDeletion(
|
||||
{
|
||||
buildOptions,
|
||||
existingFlatObjectMetadataMaps:
|
||||
validateAndBuildResult.optimisticFlatObjectMetadataMaps,
|
||||
objectMetadataToDeleteId: flatObjectMetadataToDelete.id,
|
||||
},
|
||||
);
|
||||
return {
|
||||
status: 'success',
|
||||
action: {
|
||||
type: 'create_object',
|
||||
flatFieldMetadatas: [],
|
||||
flatObjectMetadata: flatObjectMetadataToValidate,
|
||||
},
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
};
|
||||
}
|
||||
|
||||
if (validationErrors.errors.length > 0) {
|
||||
validateAndBuildResult.failed.push(validationErrors);
|
||||
continue;
|
||||
}
|
||||
protected async validateFlatEntityDeletion({
|
||||
buildOptions,
|
||||
flatEntityToValidate: flatObjectMetadataToValidate,
|
||||
optimisticFlatEntityMaps: optimisticFlatObjectMetadataMaps,
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
}: FlatEntityValidationArgs<
|
||||
FlatObjectMetadata,
|
||||
ObjectMetadataRelatedFlatEntityMaps
|
||||
>): Promise<
|
||||
FlatEntityValidationReturnType<
|
||||
WorkspaceMigrationObjectActionV2,
|
||||
FlatObjectMetadata,
|
||||
ObjectMetadataRelatedFlatEntityMaps
|
||||
>
|
||||
> {
|
||||
const validationResult =
|
||||
this.flatObjectValidatorService.validateFlatObjectMetadataDeletion({
|
||||
buildOptions,
|
||||
flatObjectMetadataToValidate,
|
||||
optimisticFlatObjectMetadataMaps,
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
});
|
||||
|
||||
validateAndBuildResult.optimisticFlatObjectMetadataMaps =
|
||||
deleteObjectFromFlatObjectMetadataMapsOrThrow({
|
||||
objectMetadataId: flatObjectMetadataToDelete.id,
|
||||
flatObjectMetadataMaps:
|
||||
validateAndBuildResult.optimisticFlatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
const deleteObjectAction = getWorkspaceMigrationV2ObjectDeleteAction(
|
||||
flatObjectMetadataToDelete,
|
||||
);
|
||||
|
||||
validateAndBuildResult.deleted.push(deleteObjectAction);
|
||||
if (validationResult.errors.length > 0) {
|
||||
return {
|
||||
status: 'fail',
|
||||
...validationResult,
|
||||
};
|
||||
}
|
||||
|
||||
for (const {
|
||||
return {
|
||||
status: 'success',
|
||||
action: {
|
||||
type: 'delete_object',
|
||||
objectMetadataId: flatObjectMetadataToValidate.id,
|
||||
},
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
};
|
||||
}
|
||||
|
||||
protected async validateFlatEntityUpdate({
|
||||
buildOptions,
|
||||
flatEntityUpdate: {
|
||||
from: fromFlatObjectMetadata,
|
||||
to: toFlatObjectMetadata,
|
||||
} of updatedFlatObjectMetadatas) {
|
||||
const objectUpdatedProperties = compareTwoFlatObjectMetadata({
|
||||
from: fromFlatObjectMetadata,
|
||||
to: toFlatObjectMetadata,
|
||||
});
|
||||
},
|
||||
optimisticFlatEntityMaps: optimisticFlatObjectMetadataMaps,
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
}: FlatEntityUpdateValidationArgs<
|
||||
FlatObjectMetadata,
|
||||
ObjectMetadataRelatedFlatEntityMaps
|
||||
>): Promise<
|
||||
| FlatEntityValidationReturnType<
|
||||
WorkspaceMigrationObjectActionV2,
|
||||
FlatObjectMetadata,
|
||||
ObjectMetadataRelatedFlatEntityMaps
|
||||
>
|
||||
| undefined
|
||||
> {
|
||||
const objectMetadataUpdatedProperties = compareTwoFlatObjectMetadata({
|
||||
fromFlatObjectMetadata,
|
||||
toFlatObjectMetadata,
|
||||
});
|
||||
|
||||
if (objectUpdatedProperties.length === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const validationErrors =
|
||||
this.flatObjectMetadataValidatorService.validateFlatObjectMetadataUpdate(
|
||||
{
|
||||
existingFlatObjectMetadataMaps:
|
||||
validateAndBuildResult.optimisticFlatObjectMetadataMaps,
|
||||
updatedFlatObjectMetadata: toFlatObjectMetadata,
|
||||
},
|
||||
);
|
||||
|
||||
if (validationErrors.errors.length > 0) {
|
||||
validateAndBuildResult.failed.push(validationErrors);
|
||||
continue;
|
||||
}
|
||||
|
||||
validateAndBuildResult.optimisticFlatObjectMetadataMaps =
|
||||
replaceFlatObjectMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadata: toFlatObjectMetadata,
|
||||
flatObjectMetadataMaps:
|
||||
validateAndBuildResult.optimisticFlatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
const updateObjectAction: UpdateObjectAction = {
|
||||
type: 'update_object',
|
||||
objectMetadataId: toFlatObjectMetadata.id,
|
||||
updates: objectUpdatedProperties,
|
||||
};
|
||||
|
||||
validateAndBuildResult.updated.push(updateObjectAction);
|
||||
if (objectMetadataUpdatedProperties.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return validateAndBuildResult;
|
||||
const validationResult =
|
||||
this.flatObjectValidatorService.validateFlatObjectMetadataUpdate({
|
||||
buildOptions,
|
||||
flatObjectMetadataToValidate: toFlatObjectMetadata,
|
||||
optimisticFlatObjectMetadataMaps,
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
});
|
||||
|
||||
if (validationResult.errors.length > 0) {
|
||||
return {
|
||||
status: 'fail',
|
||||
...validationResult,
|
||||
};
|
||||
}
|
||||
|
||||
const updateObjectAction: UpdateObjectAction = {
|
||||
type: 'update_object',
|
||||
objectMetadataId: toFlatObjectMetadata.id,
|
||||
updates: objectMetadataUpdatedProperties,
|
||||
};
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
action: updateObjectAction,
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+13
-24
@@ -12,31 +12,26 @@ import {
|
||||
UpdateServerlessFunctionAction,
|
||||
WorkspaceMigrationServerlessFunctionActionV2,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-serverless-function-action-v2.type';
|
||||
import {
|
||||
FlatServerlessFunctionValidatorService,
|
||||
ServerlessFunctionRelatedFlatEntityMaps,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-serverless-function-validator.service';
|
||||
import { FlatServerlessFunctionValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-serverless-function-validator.service';
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceMigrationV2ServerlessFunctionActionsBuilderService extends WorkspaceEntityMigrationBuilderV2Service<
|
||||
'serverlessFunction',
|
||||
FlatServerlessFunction,
|
||||
WorkspaceMigrationServerlessFunctionActionV2,
|
||||
ServerlessFunctionRelatedFlatEntityMaps
|
||||
undefined
|
||||
> {
|
||||
constructor(
|
||||
private readonly flatServerlessFunctionValidatorService: FlatServerlessFunctionValidatorService,
|
||||
) {
|
||||
super();
|
||||
super('serverlessFunction');
|
||||
}
|
||||
|
||||
protected async validateFlatEntityCreation({
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
flatEntityToValidate: flatServerlessFunctionToValidate,
|
||||
optimisticFlatEntityMaps: optimisticFlatServerlessFunctionMaps,
|
||||
}: FlatEntityValidationArgs<
|
||||
FlatServerlessFunction,
|
||||
ServerlessFunctionRelatedFlatEntityMaps
|
||||
>): Promise<
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
}: FlatEntityValidationArgs<FlatServerlessFunction>): Promise<
|
||||
FlatEntityValidationReturnType<
|
||||
WorkspaceMigrationServerlessFunctionActionV2,
|
||||
FlatServerlessFunction
|
||||
@@ -47,7 +42,6 @@ export class WorkspaceMigrationV2ServerlessFunctionActionsBuilderService extends
|
||||
{
|
||||
flatServerlessFunctionToValidate,
|
||||
optimisticFlatServerlessFunctionMaps,
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -64,17 +58,15 @@ export class WorkspaceMigrationV2ServerlessFunctionActionsBuilderService extends
|
||||
type: 'create_serverless_function',
|
||||
serverlessFunction: flatServerlessFunctionToValidate,
|
||||
},
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
};
|
||||
}
|
||||
|
||||
protected async validateFlatEntityDeletion({
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
flatEntityToValidate: flatServerlessFunctionToValidate,
|
||||
optimisticFlatEntityMaps: optimisticFlatServerlessFunctionMaps,
|
||||
}: FlatEntityValidationArgs<
|
||||
FlatServerlessFunction,
|
||||
ServerlessFunctionRelatedFlatEntityMaps
|
||||
>): Promise<
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
}: FlatEntityValidationArgs<FlatServerlessFunction>): Promise<
|
||||
FlatEntityValidationReturnType<
|
||||
WorkspaceMigrationServerlessFunctionActionV2,
|
||||
FlatServerlessFunction
|
||||
@@ -85,7 +77,6 @@ export class WorkspaceMigrationV2ServerlessFunctionActionsBuilderService extends
|
||||
{
|
||||
flatServerlessFunctionToValidate,
|
||||
optimisticFlatServerlessFunctionMaps,
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -102,20 +93,18 @@ export class WorkspaceMigrationV2ServerlessFunctionActionsBuilderService extends
|
||||
type: 'delete_serverless_function',
|
||||
serverlessFunctionId: flatServerlessFunctionToValidate.id,
|
||||
},
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
};
|
||||
}
|
||||
|
||||
protected async validateFlatEntityUpdate({
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
flatEntityUpdate: {
|
||||
from: fromFlatServerlessFunction,
|
||||
to: toFlatServerlessFunction,
|
||||
},
|
||||
optimisticFlatEntityMaps: optimisticFlatServerlessFunctionMaps,
|
||||
}: FlatEntityUpdateValidationArgs<
|
||||
FlatServerlessFunction,
|
||||
ServerlessFunctionRelatedFlatEntityMaps
|
||||
>): Promise<
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
}: FlatEntityUpdateValidationArgs<FlatServerlessFunction>): Promise<
|
||||
| FlatEntityValidationReturnType<
|
||||
WorkspaceMigrationServerlessFunctionActionV2,
|
||||
FlatServerlessFunction
|
||||
@@ -137,7 +126,6 @@ export class WorkspaceMigrationV2ServerlessFunctionActionsBuilderService extends
|
||||
{
|
||||
flatServerlessFunctionToValidate: toFlatServerlessFunction,
|
||||
optimisticFlatServerlessFunctionMaps,
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -158,6 +146,7 @@ export class WorkspaceMigrationV2ServerlessFunctionActionsBuilderService extends
|
||||
return {
|
||||
status: 'success',
|
||||
action: updateServerlessFunctionAction,
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -2,5 +2,5 @@ import { type AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all
|
||||
|
||||
export type ViewFieldRelatedFlatEntityMaps = Pick<
|
||||
AllFlatEntityMaps,
|
||||
'flatObjectMetadataMaps' | 'flatViewMaps'
|
||||
'flatViewMaps' | 'flatFieldMetadataMaps'
|
||||
>;
|
||||
|
||||
+52
-4
@@ -1,5 +1,10 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/core-modules/common/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
|
||||
import { replaceFlatEntityInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/replace-flat-entity-in-flat-entity-maps-or-throw.util';
|
||||
import { FlatViewField } from 'src/engine/core-modules/view/flat-view/types/flat-view-field.type';
|
||||
import { compareTwoFlatViewField } from 'src/engine/core-modules/view/flat-view/utils/compare-two-flat-view-field.util';
|
||||
import { ViewFieldRelatedFlatEntityMaps } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/types/view-field-related-flat-entity-maps.type';
|
||||
@@ -17,6 +22,7 @@ import { FlatViewFieldValidatorService } from 'src/engine/workspace-manager/work
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceMigrationV2ViewFieldActionsBuilderService extends WorkspaceEntityMigrationBuilderV2Service<
|
||||
'viewField',
|
||||
FlatViewField,
|
||||
WorkspaceMigrationViewFieldActionV2,
|
||||
ViewFieldRelatedFlatEntityMaps
|
||||
@@ -24,7 +30,7 @@ export class WorkspaceMigrationV2ViewFieldActionsBuilderService extends Workspac
|
||||
constructor(
|
||||
private readonly flatViewFieldValidatorService: FlatViewFieldValidatorService,
|
||||
) {
|
||||
super();
|
||||
super('viewField');
|
||||
}
|
||||
|
||||
protected async validateFlatEntityCreation({
|
||||
@@ -37,7 +43,8 @@ export class WorkspaceMigrationV2ViewFieldActionsBuilderService extends Workspac
|
||||
>): Promise<
|
||||
FlatEntityValidationReturnType<
|
||||
WorkspaceMigrationViewFieldActionV2,
|
||||
FlatViewField
|
||||
FlatViewField,
|
||||
ViewFieldRelatedFlatEntityMaps
|
||||
>
|
||||
> {
|
||||
const validationResult =
|
||||
@@ -54,12 +61,29 @@ export class WorkspaceMigrationV2ViewFieldActionsBuilderService extends Workspac
|
||||
};
|
||||
}
|
||||
|
||||
const flatView = findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: flatViewFieldToValidate.viewId,
|
||||
flatEntityMaps: dependencyOptimisticFlatEntityMaps.flatViewMaps,
|
||||
});
|
||||
|
||||
const updatedFlatViewFields = replaceFlatEntityInFlatEntityMapsOrThrow({
|
||||
flatEntity: {
|
||||
...flatView,
|
||||
viewFieldIds: [...flatView.viewFieldIds, flatViewFieldToValidate.id],
|
||||
},
|
||||
flatEntityMaps: dependencyOptimisticFlatEntityMaps.flatViewMaps,
|
||||
});
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
action: {
|
||||
type: 'create_view_field',
|
||||
viewField: flatViewFieldToValidate,
|
||||
},
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
...dependencyOptimisticFlatEntityMaps,
|
||||
flatViewMaps: updatedFlatViewFields,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -73,7 +97,8 @@ export class WorkspaceMigrationV2ViewFieldActionsBuilderService extends Workspac
|
||||
>): Promise<
|
||||
FlatEntityValidationReturnType<
|
||||
WorkspaceMigrationViewFieldActionV2,
|
||||
FlatViewField
|
||||
FlatViewField,
|
||||
ViewFieldRelatedFlatEntityMaps
|
||||
>
|
||||
> {
|
||||
const validationResult =
|
||||
@@ -90,12 +115,33 @@ export class WorkspaceMigrationV2ViewFieldActionsBuilderService extends Workspac
|
||||
};
|
||||
}
|
||||
|
||||
const flatView = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: flatViewFieldToValidate.viewId,
|
||||
flatEntityMaps: dependencyOptimisticFlatEntityMaps.flatViewMaps,
|
||||
});
|
||||
|
||||
const updatedFlatViewFields = isDefined(flatView)
|
||||
? replaceFlatEntityInFlatEntityMapsOrThrow({
|
||||
flatEntity: {
|
||||
...flatView,
|
||||
viewFieldIds: flatView.viewFieldIds.filter(
|
||||
(id) => id !== flatViewFieldToValidate.id,
|
||||
),
|
||||
},
|
||||
flatEntityMaps: dependencyOptimisticFlatEntityMaps.flatViewMaps,
|
||||
})
|
||||
: dependencyOptimisticFlatEntityMaps.flatViewMaps;
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
action: {
|
||||
type: 'delete_view_field',
|
||||
viewFieldId: flatViewFieldToValidate.id,
|
||||
},
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
...dependencyOptimisticFlatEntityMaps,
|
||||
flatViewMaps: updatedFlatViewFields,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -109,7 +155,8 @@ export class WorkspaceMigrationV2ViewFieldActionsBuilderService extends Workspac
|
||||
>): Promise<
|
||||
| FlatEntityValidationReturnType<
|
||||
WorkspaceMigrationViewFieldActionV2,
|
||||
FlatViewField
|
||||
FlatViewField,
|
||||
ViewFieldRelatedFlatEntityMaps
|
||||
>
|
||||
| undefined
|
||||
> {
|
||||
@@ -145,6 +192,7 @@ export class WorkspaceMigrationV2ViewFieldActionsBuilderService extends Workspac
|
||||
return {
|
||||
status: 'success',
|
||||
action: updateViewFieldAction,
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+20
-5
@@ -18,10 +18,10 @@ import { FlatViewValidatorService } from 'src/engine/workspace-manager/workspace
|
||||
export type ViewRelatedFlatEntityMaps = Pick<
|
||||
AllFlatEntityMaps,
|
||||
'flatObjectMetadataMaps'
|
||||
// 'flatFieldMetadataMaps' TODO implement once with extract field from object diffing
|
||||
>;
|
||||
@Injectable()
|
||||
export class WorkspaceMigrationV2ViewActionsBuilderService extends WorkspaceEntityMigrationBuilderV2Service<
|
||||
'view',
|
||||
FlatView,
|
||||
WorkspaceMigrationViewActionV2,
|
||||
ViewRelatedFlatEntityMaps
|
||||
@@ -29,7 +29,7 @@ export class WorkspaceMigrationV2ViewActionsBuilderService extends WorkspaceEnti
|
||||
constructor(
|
||||
private readonly flatViewValidatorService: FlatViewValidatorService,
|
||||
) {
|
||||
super();
|
||||
super('view');
|
||||
}
|
||||
|
||||
protected async validateFlatEntityCreation({
|
||||
@@ -37,7 +37,11 @@ export class WorkspaceMigrationV2ViewActionsBuilderService extends WorkspaceEnti
|
||||
flatEntityToValidate: flatViewToValidate,
|
||||
optimisticFlatEntityMaps: optimisticFlatViewMaps,
|
||||
}: FlatEntityValidationArgs<FlatView, ViewRelatedFlatEntityMaps>): Promise<
|
||||
FlatEntityValidationReturnType<WorkspaceMigrationViewActionV2, FlatView>
|
||||
FlatEntityValidationReturnType<
|
||||
WorkspaceMigrationViewActionV2,
|
||||
FlatView,
|
||||
ViewRelatedFlatEntityMaps
|
||||
>
|
||||
> {
|
||||
const validationResult =
|
||||
await this.flatViewValidatorService.validateFlatViewCreation({
|
||||
@@ -59,6 +63,7 @@ export class WorkspaceMigrationV2ViewActionsBuilderService extends WorkspaceEnti
|
||||
type: 'create_view',
|
||||
view: flatViewToValidate,
|
||||
},
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -67,7 +72,11 @@ export class WorkspaceMigrationV2ViewActionsBuilderService extends WorkspaceEnti
|
||||
flatEntityToValidate: flatViewToValidate,
|
||||
optimisticFlatEntityMaps: optimisticFlatViewMaps,
|
||||
}: FlatEntityValidationArgs<FlatView, ViewRelatedFlatEntityMaps>): Promise<
|
||||
FlatEntityValidationReturnType<WorkspaceMigrationViewActionV2, FlatView>
|
||||
FlatEntityValidationReturnType<
|
||||
WorkspaceMigrationViewActionV2,
|
||||
FlatView,
|
||||
ViewRelatedFlatEntityMaps
|
||||
>
|
||||
> {
|
||||
const validationResult =
|
||||
this.flatViewValidatorService.validateFlatViewDeletion({
|
||||
@@ -89,6 +98,7 @@ export class WorkspaceMigrationV2ViewActionsBuilderService extends WorkspaceEnti
|
||||
type: 'delete_view',
|
||||
viewId: flatViewToValidate.id,
|
||||
},
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -100,7 +110,11 @@ export class WorkspaceMigrationV2ViewActionsBuilderService extends WorkspaceEnti
|
||||
FlatView,
|
||||
ViewRelatedFlatEntityMaps
|
||||
>): Promise<
|
||||
| FlatEntityValidationReturnType<WorkspaceMigrationViewActionV2, FlatView>
|
||||
| FlatEntityValidationReturnType<
|
||||
WorkspaceMigrationViewActionV2,
|
||||
FlatView,
|
||||
ViewRelatedFlatEntityMaps
|
||||
>
|
||||
| undefined
|
||||
> {
|
||||
const viewUpdatedProperties = compareTwoFlatView({
|
||||
@@ -135,6 +149,7 @@ export class WorkspaceMigrationV2ViewActionsBuilderService extends WorkspaceEnti
|
||||
return {
|
||||
status: 'success',
|
||||
action: updateViewAction,
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+247
-39
@@ -1,21 +1,29 @@
|
||||
import { Inject } from '@nestjs/common';
|
||||
|
||||
import { type FromTo } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import {
|
||||
FlatEntityMapsException,
|
||||
FlatEntityMapsExceptionCode,
|
||||
} from 'src/engine/core-modules/common/exceptions/flat-entity-maps.exception';
|
||||
import { AllFlatEntitiesByMetadataEngineName } from 'src/engine/core-modules/common/types/all-flat-entities-by-metadata-engine-name.type';
|
||||
import { type AllFlatEntities } from 'src/engine/core-modules/common/types/all-flat-entities.type';
|
||||
import { type AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { type FlatEntityMaps } from 'src/engine/core-modules/common/types/flat-entity-maps.type';
|
||||
import { type FlatEntity } from 'src/engine/core-modules/common/types/flat-entity.type';
|
||||
import { addFlatEntityToFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/add-flat-entity-to-flat-entity-maps-or-throw.util';
|
||||
import { deleteFlatEntityFromFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/delete-flat-entity-from-flat-entity-maps-or-throw.util';
|
||||
import { replaceFlatEntityInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/replace-flat-entity-in-flat-entity-maps-or-throw.util';
|
||||
import { LoggerService } from 'src/engine/core-modules/logger/logger.service';
|
||||
import {
|
||||
type DeletedCreatedUpdatedMatrix,
|
||||
deletedCreatedUpdatedMatrixDispatcher,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/utils/deleted-created-updated-matrix-dispatcher.util';
|
||||
flatEntityDeletedCreatedUpdatedMatrixDispatcher,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/utils/flat-entity-deleted-created-updated-matrix-dispatcher.util';
|
||||
import { type FailedFlatEntityValidation } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/types/failed-flat-entity-validation.type';
|
||||
import { type WorkspaceMigrationV2BuilderOptions } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/services/workspace-migration-builder-v2.service';
|
||||
import { type WorkspaceMigrationActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-action-common-v2';
|
||||
import { type WorkspaceMigrationBuilderOptions } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-builder-options.type';
|
||||
|
||||
// TODO extract types in dedicated files
|
||||
export type CreatedDeletedUpdatedActions<
|
||||
TActions extends WorkspaceMigrationActionV2,
|
||||
> = {
|
||||
@@ -26,91 +34,140 @@ export type CreatedDeletedUpdatedActions<
|
||||
|
||||
export type SuccessfulEntityMigrationBuildResult<
|
||||
TActions extends WorkspaceMigrationActionV2,
|
||||
TFlatEntity extends FlatEntity,
|
||||
TFlatEntity extends AllFlatEntities,
|
||||
TRelatedFlatEntityMaps extends
|
||||
| Partial<AllFlatEntityMaps>
|
||||
| undefined = undefined,
|
||||
> = {
|
||||
status: 'success';
|
||||
actions: CreatedDeletedUpdatedActions<TActions>;
|
||||
optimisticFlatEntityMaps: FlatEntityMaps<TFlatEntity>;
|
||||
dependencyOptimisticFlatEntityMaps: TRelatedFlatEntityMaps;
|
||||
};
|
||||
|
||||
export type FailedEntityMigrationBuildResult<TFlatEntity extends FlatEntity> = {
|
||||
export type FailedEntityMigrationBuildResult<
|
||||
TFlatEntity extends AllFlatEntities,
|
||||
TRelatedFlatEntityMaps extends
|
||||
| Partial<AllFlatEntityMaps>
|
||||
| undefined = undefined,
|
||||
> = {
|
||||
status: 'fail';
|
||||
errors: FailedFlatEntityValidation<TFlatEntity>[];
|
||||
optimisticFlatEntityMaps: FlatEntityMaps<TFlatEntity>;
|
||||
dependencyOptimisticFlatEntityMaps: TRelatedFlatEntityMaps;
|
||||
};
|
||||
|
||||
export type ValidateAndBuildArgs<
|
||||
T extends FlatEntity,
|
||||
TRelatedFlatEntityMaps extends Partial<AllFlatEntityMaps>,
|
||||
T extends AllFlatEntities,
|
||||
TRelatedFlatEntityMaps extends Partial<AllFlatEntityMaps> | undefined,
|
||||
> = {
|
||||
buildOptions: WorkspaceMigrationV2BuilderOptions;
|
||||
buildOptions: WorkspaceMigrationBuilderOptions;
|
||||
dependencyOptimisticFlatEntityMaps: TRelatedFlatEntityMaps;
|
||||
workspaceId: string;
|
||||
} & FromTo<FlatEntityMaps<T>>;
|
||||
|
||||
export type ValidateAndBuildReturnType<
|
||||
TActions extends WorkspaceMigrationActionV2,
|
||||
TFlatEntity extends FlatEntity,
|
||||
TFlatEntity extends AllFlatEntities,
|
||||
TRelatedFlatEntityMaps extends
|
||||
| Partial<AllFlatEntityMaps>
|
||||
| undefined = undefined,
|
||||
> =
|
||||
| SuccessfulEntityMigrationBuildResult<TActions, TFlatEntity>
|
||||
| FailedEntityMigrationBuildResult<TFlatEntity>;
|
||||
| SuccessfulEntityMigrationBuildResult<
|
||||
TActions,
|
||||
TFlatEntity,
|
||||
TRelatedFlatEntityMaps
|
||||
>
|
||||
| FailedEntityMigrationBuildResult<TFlatEntity, TRelatedFlatEntityMaps>;
|
||||
|
||||
export type ValidateAndBuildActionsArgs<
|
||||
T extends FlatEntity,
|
||||
T extends AllFlatEntities,
|
||||
TRelatedFlatEntityMaps extends Partial<AllFlatEntityMaps>,
|
||||
> = ValidateAndBuildArgs<T, TRelatedFlatEntityMaps> &
|
||||
DeletedCreatedUpdatedMatrix<T>;
|
||||
|
||||
export type ValidateAndBuilActionsReturnType<
|
||||
TFlatEntity extends FlatEntity,
|
||||
TFlatEntity extends AllFlatEntities,
|
||||
TActions extends WorkspaceMigrationActionV2,
|
||||
> = {
|
||||
failed: FailedFlatEntityValidation<TFlatEntity>[];
|
||||
} & CreatedDeletedUpdatedActions<TActions>;
|
||||
|
||||
export type FlatEntityValidationArgs<
|
||||
TFlatEntity extends FlatEntity,
|
||||
TRelatedFlatEntityMaps extends Partial<AllFlatEntityMaps>,
|
||||
TFlatEntity extends AllFlatEntities,
|
||||
TRelatedFlatEntityMaps extends
|
||||
| Partial<AllFlatEntityMaps>
|
||||
| undefined = undefined,
|
||||
> = {
|
||||
flatEntityToValidate: TFlatEntity;
|
||||
optimisticFlatEntityMaps: FlatEntityMaps<TFlatEntity>;
|
||||
dependencyOptimisticFlatEntityMaps: TRelatedFlatEntityMaps;
|
||||
workspaceId: string;
|
||||
remainingFlatEntityMapsToValidate: FlatEntityMaps<TFlatEntity>;
|
||||
buildOptions: WorkspaceMigrationBuilderOptions;
|
||||
};
|
||||
|
||||
export type FlatEntityUpdateValidationArgs<
|
||||
TFlatEntity extends FlatEntity,
|
||||
TRelatedFlatEntityMaps extends Partial<AllFlatEntityMaps>,
|
||||
TFlatEntity extends AllFlatEntities,
|
||||
TRelatedFlatEntityMaps extends
|
||||
| Partial<AllFlatEntityMaps>
|
||||
| undefined = undefined,
|
||||
> = Omit<
|
||||
FlatEntityValidationArgs<TFlatEntity, TRelatedFlatEntityMaps>,
|
||||
'flatEntityToValidate'
|
||||
'flatEntityToValidate' | 'remainingFlatEntityMapsToValidate'
|
||||
> & {
|
||||
flatEntityUpdate: FromTo<TFlatEntity>;
|
||||
remainingFlatEntityMapsToValidate: FromTo<FlatEntityMaps<TFlatEntity>>;
|
||||
};
|
||||
|
||||
export type FlatEntityValidationReturnType<
|
||||
TActions extends WorkspaceMigrationActionV2,
|
||||
TFlatEntity extends FlatEntity,
|
||||
TFlatEntity extends AllFlatEntities,
|
||||
TRelatedFlatEntityMaps extends
|
||||
| Partial<AllFlatEntityMaps>
|
||||
| undefined = undefined,
|
||||
> =
|
||||
| {
|
||||
status: 'success';
|
||||
action: TActions | TActions[];
|
||||
dependencyOptimisticFlatEntityMaps: TRelatedFlatEntityMaps;
|
||||
}
|
||||
| ({
|
||||
status: 'fail';
|
||||
} & FailedFlatEntityValidation<TFlatEntity>);
|
||||
|
||||
// TODO prastoin refactor the builder to building a selection of flatEntityMaps => simplify
|
||||
export abstract class WorkspaceEntityMigrationBuilderV2Service<
|
||||
T extends keyof AllFlatEntitiesByMetadataEngineName, // TODO refactor the whole generic to be inferred from this one only
|
||||
TFlatEntity extends AllFlatEntities,
|
||||
TActions extends WorkspaceMigrationActionV2,
|
||||
TRelatedFlatEntityMaps extends Partial<AllFlatEntityMaps>,
|
||||
TRelatedFlatEntityMaps extends
|
||||
| Partial<AllFlatEntityMaps>
|
||||
| undefined = undefined,
|
||||
> {
|
||||
@Inject(LoggerService)
|
||||
protected readonly logger: LoggerService;
|
||||
private metadataName: T;
|
||||
|
||||
constructor(metadataName: T) {
|
||||
this.metadataName = metadataName;
|
||||
}
|
||||
|
||||
public async validateAndBuild({
|
||||
buildOptions,
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
from: fromFlatEntityMaps,
|
||||
to: toFlatEntityMaps,
|
||||
workspaceId,
|
||||
}: ValidateAndBuildArgs<TFlatEntity, TRelatedFlatEntityMaps>): Promise<
|
||||
ValidateAndBuildReturnType<TActions, TFlatEntity>
|
||||
ValidateAndBuildReturnType<TActions, TFlatEntity, TRelatedFlatEntityMaps>
|
||||
> {
|
||||
this.logger.time(`EntityBuilder ${this.metadataName}`, 'validateAndBuild');
|
||||
this.logger.time(
|
||||
`EntityBuilder ${this.metadataName}`,
|
||||
'matrix computation',
|
||||
);
|
||||
|
||||
const fromFlatEntities = Object.values(fromFlatEntityMaps.byId).filter(
|
||||
isDefined,
|
||||
);
|
||||
@@ -118,14 +175,23 @@ export abstract class WorkspaceEntityMigrationBuilderV2Service<
|
||||
isDefined,
|
||||
);
|
||||
|
||||
const { created, deleted, updated } = deletedCreatedUpdatedMatrixDispatcher(
|
||||
{
|
||||
from: fromFlatEntities,
|
||||
to: toFlatEntities,
|
||||
},
|
||||
);
|
||||
let optimisticFlatEntityMaps = structuredClone(fromFlatEntityMaps);
|
||||
const {
|
||||
createdFlatEntityMaps,
|
||||
deletedFlatEntityMaps,
|
||||
updatedFlatEntityMaps,
|
||||
} = flatEntityDeletedCreatedUpdatedMatrixDispatcher({
|
||||
from: fromFlatEntities,
|
||||
to: toFlatEntities,
|
||||
buildOptions,
|
||||
});
|
||||
|
||||
this.logger.timeEnd(
|
||||
`EntityBuilder ${this.metadataName}`,
|
||||
'matrix computation',
|
||||
);
|
||||
this.logger.time(`EntityBuilder ${this.metadataName}`, 'entity processing');
|
||||
|
||||
let optimisticFlatEntityMaps = structuredClone(fromFlatEntityMaps);
|
||||
const validateAndBuildResult: ValidateAndBuilActionsReturnType<
|
||||
TFlatEntity,
|
||||
TActions
|
||||
@@ -136,11 +202,38 @@ export abstract class WorkspaceEntityMigrationBuilderV2Service<
|
||||
updated: [],
|
||||
};
|
||||
|
||||
for (const flatEntityToCreate of created) {
|
||||
let remainingFlatEntityMapsToCreate = structuredClone(
|
||||
createdFlatEntityMaps,
|
||||
);
|
||||
|
||||
this.logger.time(
|
||||
`EntityBuilder ${this.metadataName}`,
|
||||
'creation validation',
|
||||
);
|
||||
for (const flatEntityToCreateId in createdFlatEntityMaps.byId) {
|
||||
const flatEntityToCreate =
|
||||
createdFlatEntityMaps.byId[flatEntityToCreateId];
|
||||
|
||||
if (!isDefined(flatEntityToCreate)) {
|
||||
throw new FlatEntityMapsException(
|
||||
'Could not find flat entity to create in maps should never occur',
|
||||
FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
remainingFlatEntityMapsToCreate =
|
||||
deleteFlatEntityFromFlatEntityMapsOrThrow({
|
||||
entityToDeleteId: flatEntityToCreateId,
|
||||
flatEntityMaps: remainingFlatEntityMapsToCreate,
|
||||
});
|
||||
|
||||
const validationResult = await this.validateFlatEntityCreation({
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
flatEntityToValidate: flatEntityToCreate,
|
||||
optimisticFlatEntityMaps,
|
||||
workspaceId,
|
||||
remainingFlatEntityMapsToValidate: remainingFlatEntityMapsToCreate,
|
||||
buildOptions,
|
||||
});
|
||||
|
||||
if (validationResult.status === 'fail') {
|
||||
@@ -152,6 +245,8 @@ export abstract class WorkspaceEntityMigrationBuilderV2Service<
|
||||
flatEntity: flatEntityToCreate,
|
||||
flatEntityMaps: optimisticFlatEntityMaps,
|
||||
});
|
||||
dependencyOptimisticFlatEntityMaps =
|
||||
validationResult.dependencyOptimisticFlatEntityMaps;
|
||||
|
||||
validateAndBuildResult.created.push(
|
||||
...(Array.isArray(validationResult.action)
|
||||
@@ -160,13 +255,45 @@ export abstract class WorkspaceEntityMigrationBuilderV2Service<
|
||||
);
|
||||
}
|
||||
|
||||
for (const flatEntityToDelete of buildOptions.inferDeletionFromMissingEntities
|
||||
? deleted
|
||||
: []) {
|
||||
this.logger.timeEnd(
|
||||
`EntityBuilder ${this.metadataName}`,
|
||||
'creation validation',
|
||||
);
|
||||
this.logger.time(
|
||||
`EntityBuilder ${this.metadataName}`,
|
||||
'deletion validation',
|
||||
);
|
||||
|
||||
let remainingFlatEntityMapsToDelete = structuredClone(
|
||||
deletedFlatEntityMaps,
|
||||
);
|
||||
|
||||
for (const flatEntityToDeleteId in buildOptions.inferDeletionFromMissingEntities
|
||||
? deletedFlatEntityMaps.byId
|
||||
: {}) {
|
||||
const flatEntityToDelete =
|
||||
deletedFlatEntityMaps.byId[flatEntityToDeleteId];
|
||||
|
||||
if (!isDefined(flatEntityToDelete)) {
|
||||
throw new FlatEntityMapsException(
|
||||
'Could not find flat entity to delete in maps should never occur',
|
||||
FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
remainingFlatEntityMapsToDelete =
|
||||
deleteFlatEntityFromFlatEntityMapsOrThrow({
|
||||
entityToDeleteId: flatEntityToDeleteId,
|
||||
flatEntityMaps: remainingFlatEntityMapsToDelete,
|
||||
});
|
||||
|
||||
const validationResult = await this.validateFlatEntityDeletion({
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
flatEntityToValidate: flatEntityToDelete,
|
||||
optimisticFlatEntityMaps: optimisticFlatEntityMaps,
|
||||
workspaceId,
|
||||
remainingFlatEntityMapsToValidate: remainingFlatEntityMapsToDelete,
|
||||
buildOptions,
|
||||
});
|
||||
|
||||
if (validationResult.status === 'fail') {
|
||||
@@ -178,6 +305,8 @@ export abstract class WorkspaceEntityMigrationBuilderV2Service<
|
||||
entityToDeleteId: flatEntityToDelete.id,
|
||||
flatEntityMaps: optimisticFlatEntityMaps,
|
||||
});
|
||||
dependencyOptimisticFlatEntityMaps =
|
||||
validationResult.dependencyOptimisticFlatEntityMaps;
|
||||
|
||||
validateAndBuildResult.deleted.push(
|
||||
...(Array.isArray(validationResult.action)
|
||||
@@ -186,11 +315,53 @@ export abstract class WorkspaceEntityMigrationBuilderV2Service<
|
||||
);
|
||||
}
|
||||
|
||||
for (const flatEntityUpdate of updated) {
|
||||
this.logger.timeEnd(
|
||||
`EntityBuilder ${this.metadataName}`,
|
||||
'deletion validation',
|
||||
);
|
||||
this.logger.time(`EntityBuilder ${this.metadataName}`, 'update validation');
|
||||
|
||||
let remainingFlatEntityMapsToUpdate = structuredClone(
|
||||
updatedFlatEntityMaps,
|
||||
);
|
||||
|
||||
for (const flatEntityToUpdateId in updatedFlatEntityMaps.to.byId) {
|
||||
const flatEntityToUpdateFrom =
|
||||
updatedFlatEntityMaps.from.byId[flatEntityToUpdateId];
|
||||
const flatEntityToUpdateTo =
|
||||
updatedFlatEntityMaps.to.byId[flatEntityToUpdateId];
|
||||
|
||||
if (
|
||||
!isDefined(flatEntityToUpdateTo) ||
|
||||
!isDefined(flatEntityToUpdateFrom)
|
||||
) {
|
||||
throw new FlatEntityMapsException(
|
||||
'Could not find flat entity to update in maps should never occur',
|
||||
FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
remainingFlatEntityMapsToUpdate.from =
|
||||
deleteFlatEntityFromFlatEntityMapsOrThrow({
|
||||
entityToDeleteId: flatEntityToUpdateId,
|
||||
flatEntityMaps: remainingFlatEntityMapsToUpdate.from,
|
||||
});
|
||||
remainingFlatEntityMapsToUpdate.to =
|
||||
deleteFlatEntityFromFlatEntityMapsOrThrow({
|
||||
entityToDeleteId: flatEntityToUpdateId,
|
||||
flatEntityMaps: remainingFlatEntityMapsToUpdate.to,
|
||||
});
|
||||
|
||||
const validationResult = await this.validateFlatEntityUpdate({
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
flatEntityUpdate,
|
||||
flatEntityUpdate: {
|
||||
from: flatEntityToUpdateFrom,
|
||||
to: flatEntityToUpdateTo,
|
||||
},
|
||||
optimisticFlatEntityMaps: optimisticFlatEntityMaps,
|
||||
workspaceId,
|
||||
remainingFlatEntityMapsToValidate: remainingFlatEntityMapsToUpdate,
|
||||
buildOptions,
|
||||
});
|
||||
|
||||
if (validationResult === undefined) {
|
||||
@@ -203,9 +374,11 @@ export abstract class WorkspaceEntityMigrationBuilderV2Service<
|
||||
}
|
||||
|
||||
optimisticFlatEntityMaps = replaceFlatEntityInFlatEntityMapsOrThrow({
|
||||
flatEntity: flatEntityUpdate.to,
|
||||
flatEntity: flatEntityToUpdateTo,
|
||||
flatEntityMaps: optimisticFlatEntityMaps,
|
||||
});
|
||||
dependencyOptimisticFlatEntityMaps =
|
||||
validationResult.dependencyOptimisticFlatEntityMaps;
|
||||
|
||||
validateAndBuildResult.updated.push(
|
||||
...(Array.isArray(validationResult.action)
|
||||
@@ -214,14 +387,29 @@ export abstract class WorkspaceEntityMigrationBuilderV2Service<
|
||||
);
|
||||
}
|
||||
|
||||
this.logger.timeEnd(
|
||||
`EntityBuilder ${this.metadataName}`,
|
||||
'update validation',
|
||||
);
|
||||
this.logger.timeEnd(
|
||||
`EntityBuilder ${this.metadataName}`,
|
||||
'entity processing',
|
||||
);
|
||||
|
||||
if (validateAndBuildResult.failed.length > 0) {
|
||||
return {
|
||||
status: 'fail',
|
||||
errors: validateAndBuildResult.failed,
|
||||
optimisticFlatEntityMaps,
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
};
|
||||
}
|
||||
|
||||
this.logger.timeEnd(
|
||||
`EntityBuilder ${this.metadataName}`,
|
||||
'validateAndBuild',
|
||||
);
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
actions: {
|
||||
@@ -230,18 +418,38 @@ export abstract class WorkspaceEntityMigrationBuilderV2Service<
|
||||
updated: validateAndBuildResult.updated,
|
||||
},
|
||||
optimisticFlatEntityMaps,
|
||||
dependencyOptimisticFlatEntityMaps,
|
||||
};
|
||||
}
|
||||
|
||||
protected abstract validateFlatEntityCreation(
|
||||
args: FlatEntityValidationArgs<TFlatEntity, TRelatedFlatEntityMaps>,
|
||||
): Promise<FlatEntityValidationReturnType<TActions, TFlatEntity>>;
|
||||
): Promise<
|
||||
FlatEntityValidationReturnType<
|
||||
TActions,
|
||||
TFlatEntity,
|
||||
TRelatedFlatEntityMaps
|
||||
>
|
||||
>;
|
||||
|
||||
protected abstract validateFlatEntityDeletion(
|
||||
args: FlatEntityValidationArgs<TFlatEntity, TRelatedFlatEntityMaps>,
|
||||
): Promise<FlatEntityValidationReturnType<TActions, TFlatEntity>>;
|
||||
): Promise<
|
||||
FlatEntityValidationReturnType<
|
||||
TActions,
|
||||
TFlatEntity,
|
||||
TRelatedFlatEntityMaps
|
||||
>
|
||||
>;
|
||||
|
||||
protected abstract validateFlatEntityUpdate(
|
||||
args: FlatEntityUpdateValidationArgs<TFlatEntity, TRelatedFlatEntityMaps>,
|
||||
): Promise<FlatEntityValidationReturnType<TActions, TFlatEntity> | undefined>;
|
||||
): Promise<
|
||||
| FlatEntityValidationReturnType<
|
||||
TActions,
|
||||
TFlatEntity,
|
||||
TRelatedFlatEntityMaps
|
||||
>
|
||||
| undefined
|
||||
>;
|
||||
}
|
||||
|
||||
-132
@@ -1,132 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { type FromTo } from 'twenty-shared/types';
|
||||
|
||||
import { FlatEntity } from 'src/engine/core-modules/common/types/flat-entity.type';
|
||||
import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
import { fromFlatObjectMetadataMapsToFlatObjectMetadatas } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-flat-object-metadata-maps-to-flat-object-metadatas.util';
|
||||
import { deletedCreatedUpdatedMatrixDispatcher } from 'src/engine/workspace-manager/workspace-migration-v2/utils/deleted-created-updated-matrix-dispatcher.util';
|
||||
import { WorkspaceMigrationV2FieldActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/field/services/workspace-migration-v2-field-actions-builder.service';
|
||||
import { WorkspaceMigrationV2ObjectActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/object/services/workspace-migration-v2-object-actions-builder.service';
|
||||
import { FailedFlatEntityValidation } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/types/failed-flat-entity-validation.type';
|
||||
import { CreatedDeletedUpdatedActions } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/services/workspace-entity-migration-builder-v2.service';
|
||||
import { WorkspaceMigrationFieldActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-field-action-v2';
|
||||
import { WorkspaceMigrationObjectActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-object-action-v2';
|
||||
import { computeUpdatedObjectMetadataDeletedCreatedUpdatedFieldMatrix } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/compute-updated-object-metadata-deleted-created-updated-field-matrix.util';
|
||||
|
||||
export type WorkspaceMigrationV2BuilderOptions = {
|
||||
inferDeletionFromMissingEntities: boolean;
|
||||
isSystemBuild: boolean;
|
||||
};
|
||||
|
||||
export type SuccessfulWorkspaceMigrationBuildResult = {
|
||||
status: 'success';
|
||||
objectActions: CreatedDeletedUpdatedActions<WorkspaceMigrationObjectActionV2>;
|
||||
fieldsActions: CreatedDeletedUpdatedActions<WorkspaceMigrationFieldActionV2>;
|
||||
optimisticFlatObjectMetadataMaps: FlatObjectMetadataMaps;
|
||||
};
|
||||
|
||||
export type FailedWorkspaceMigrationBuildResult = {
|
||||
status: 'fail';
|
||||
optimisticFlatObjectMetadataMaps: FlatObjectMetadataMaps;
|
||||
errors: FailedFlatEntityValidation<FlatEntity>[];
|
||||
};
|
||||
|
||||
export type WorkspaceMigrationBuildArgs = {
|
||||
buildOptions: WorkspaceMigrationV2BuilderOptions;
|
||||
} & FromTo<FlatObjectMetadataMaps, 'FlatObjectMetadataMaps'>;
|
||||
// TODO deprecate this file
|
||||
@Injectable()
|
||||
export class WorkspaceMigrationBuilderV2Service {
|
||||
constructor(
|
||||
private readonly workspaceMigrationV2ObjectActionsBuilderService: WorkspaceMigrationV2ObjectActionsBuilderService,
|
||||
private readonly workspaceMigrationV2FieldActionsBuilderService: WorkspaceMigrationV2FieldActionsBuilderService,
|
||||
) {}
|
||||
|
||||
public async validateAndBuild({
|
||||
fromFlatObjectMetadataMaps,
|
||||
toFlatObjectMetadataMaps,
|
||||
buildOptions,
|
||||
}: WorkspaceMigrationBuildArgs): Promise<
|
||||
| FailedWorkspaceMigrationBuildResult
|
||||
| SuccessfulWorkspaceMigrationBuildResult
|
||||
> {
|
||||
const fromFlatObjectMetadatas =
|
||||
fromFlatObjectMetadataMapsToFlatObjectMetadatas(
|
||||
fromFlatObjectMetadataMaps,
|
||||
);
|
||||
const toFlatObjectMetadatas =
|
||||
fromFlatObjectMetadataMapsToFlatObjectMetadatas(toFlatObjectMetadataMaps);
|
||||
|
||||
// Dispatch
|
||||
const {
|
||||
created: createdFlatObjectMetadatas,
|
||||
deleted: deletedFlatObjectMetadatas,
|
||||
updated: updatedFlatObjectMetadatas,
|
||||
} = deletedCreatedUpdatedMatrixDispatcher({
|
||||
from: fromFlatObjectMetadatas,
|
||||
to: toFlatObjectMetadatas,
|
||||
});
|
||||
|
||||
// Should be handled separately from objects
|
||||
const objectMetadataDeletedCreatedUpdatedFields =
|
||||
computeUpdatedObjectMetadataDeletedCreatedUpdatedFieldMatrix(
|
||||
updatedFlatObjectMetadatas,
|
||||
);
|
||||
///
|
||||
|
||||
// Validate and build
|
||||
const objectActionsValidateAndBuildResult =
|
||||
await this.workspaceMigrationV2ObjectActionsBuilderService.validateAndBuildObjectActions(
|
||||
{
|
||||
fromFlatObjectMetadataMaps,
|
||||
createdFlatObjectMetadatas,
|
||||
deletedFlatObjectMetadatas,
|
||||
toFlatObjectMetadataMaps,
|
||||
updatedFlatObjectMetadatas,
|
||||
buildOptions,
|
||||
},
|
||||
);
|
||||
|
||||
const fieldActionsValidateAndBuildResult =
|
||||
await this.workspaceMigrationV2FieldActionsBuilderService.validateAndBuildFieldActions(
|
||||
{
|
||||
buildOptions,
|
||||
fromFlatObjectMetadataMaps:
|
||||
objectActionsValidateAndBuildResult.optimisticFlatObjectMetadataMaps,
|
||||
toFlatObjectMetadataMaps,
|
||||
objectMetadataDeletedCreatedUpdatedFields,
|
||||
},
|
||||
);
|
||||
|
||||
const allValidateAndBuildResultFailures = [
|
||||
...objectActionsValidateAndBuildResult.failed,
|
||||
...fieldActionsValidateAndBuildResult.failed,
|
||||
];
|
||||
|
||||
if (allValidateAndBuildResultFailures.length > 0) {
|
||||
return {
|
||||
status: 'fail',
|
||||
errors: allValidateAndBuildResultFailures,
|
||||
optimisticFlatObjectMetadataMaps:
|
||||
fieldActionsValidateAndBuildResult.optimisticFlatObjectMetadataMaps,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
fieldsActions: {
|
||||
created: fieldActionsValidateAndBuildResult.created,
|
||||
deleted: fieldActionsValidateAndBuildResult.deleted,
|
||||
updated: fieldActionsValidateAndBuildResult.updated,
|
||||
},
|
||||
objectActions: {
|
||||
created: objectActionsValidateAndBuildResult.created,
|
||||
deleted: objectActionsValidateAndBuildResult.deleted,
|
||||
updated: objectActionsValidateAndBuildResult.updated,
|
||||
},
|
||||
optimisticFlatObjectMetadataMaps:
|
||||
fieldActionsValidateAndBuildResult.optimisticFlatObjectMetadataMaps,
|
||||
};
|
||||
}
|
||||
}
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
import { type FlatEntity } from 'src/engine/core-modules/common/types/flat-entity.type';
|
||||
import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
import { type FailedFlatEntityValidation } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/types/failed-flat-entity-validation.type';
|
||||
import { type WorkspaceMigrationActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-action-common-v2';
|
||||
|
||||
export type ValidateAndBuildMetadataResult<
|
||||
T extends WorkspaceMigrationActionV2,
|
||||
> = {
|
||||
failed: FailedFlatEntityValidation<FlatEntity>[];
|
||||
created: T[];
|
||||
deleted: T[];
|
||||
updated: T[];
|
||||
optimisticFlatObjectMetadataMaps: FlatObjectMetadataMaps;
|
||||
};
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
export type WorkspaceMigrationBuilderOptions = {
|
||||
inferDeletionFromMissingEntities: boolean;
|
||||
isSystemBuild: boolean;
|
||||
};
|
||||
+2
-2
@@ -4,12 +4,12 @@ import { type PropertyUpdate } from 'src/engine/workspace-manager/workspace-migr
|
||||
|
||||
export type CreateFieldAction = {
|
||||
type: 'create_field';
|
||||
flatFieldMetadata: FlatFieldMetadata;
|
||||
objectMetadataId: string;
|
||||
flatFieldMetadatas: FlatFieldMetadata[];
|
||||
};
|
||||
|
||||
export type UpdateFieldAction = {
|
||||
type: 'update_field';
|
||||
workspaceId: string;
|
||||
fieldMetadataId: string;
|
||||
objectMetadataId: string;
|
||||
updates: Array<
|
||||
|
||||
+4
-7
@@ -1,15 +1,12 @@
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatObjectMetadataPropertiesToCompare } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata-properties-to-compare.type';
|
||||
import {
|
||||
type FlatObjectMetadata,
|
||||
type FlatObjectMetadataWithoutFields,
|
||||
} from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { type PropertyUpdate } from 'src/engine/workspace-manager/workspace-migration-v2/types/property-update.type';
|
||||
import { type CreateFieldAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-field-action-v2';
|
||||
|
||||
export type CreateObjectAction = {
|
||||
type: 'create_object';
|
||||
flatObjectMetadataWithoutFields: FlatObjectMetadataWithoutFields;
|
||||
createFieldActions: CreateFieldAction[];
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
flatFieldMetadatas: FlatFieldMetadata[];
|
||||
};
|
||||
|
||||
export type UpdateObjectAction = {
|
||||
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
import { type FromTo } from 'twenty-shared/types';
|
||||
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import {
|
||||
type CustomDeletedCreatedUpdatedMatrix,
|
||||
deletedCreatedUpdatedMatrixDispatcher,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/utils/deleted-created-updated-matrix-dispatcher.util';
|
||||
|
||||
export type UpdatedObjectMetadataDeletedCreatedUpdatedFieldMatrix = {
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
} & CustomDeletedCreatedUpdatedMatrix<'flatFieldMetadatas', FlatFieldMetadata>;
|
||||
|
||||
export const computeUpdatedObjectMetadataDeletedCreatedUpdatedFieldMatrix = (
|
||||
updatedObjectMetadata: FromTo<FlatObjectMetadata>[],
|
||||
): UpdatedObjectMetadataDeletedCreatedUpdatedFieldMatrix[] => {
|
||||
const matrixAccumulator: UpdatedObjectMetadataDeletedCreatedUpdatedFieldMatrix[] =
|
||||
[];
|
||||
|
||||
for (const { from, to } of updatedObjectMetadata) {
|
||||
const fieldMetadataMatrix = deletedCreatedUpdatedMatrixDispatcher({
|
||||
from: from.flatFieldMetadatas,
|
||||
to: to.flatFieldMetadatas,
|
||||
});
|
||||
|
||||
matrixAccumulator.push({
|
||||
flatObjectMetadata: to,
|
||||
createdFlatFieldMetadatas: fieldMetadataMatrix.created,
|
||||
deletedFlatFieldMetadatas: fieldMetadataMatrix.deleted,
|
||||
updatedFlatFieldMetadatas: fieldMetadataMatrix.updated,
|
||||
});
|
||||
}
|
||||
|
||||
return matrixAccumulator;
|
||||
};
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
import {
|
||||
type FlatObjectMetadata,
|
||||
type FlatObjectMetadataWithoutFields,
|
||||
} from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
export const fromFlatObjectMetadataToFlatObjectMetadataWithoutFields = ({
|
||||
flatFieldMetadatas: _flatFieldMetadatas,
|
||||
...rest
|
||||
}: FlatObjectMetadata): FlatObjectMetadataWithoutFields => rest;
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
import { type FlatObjectMetadataWithoutFields } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { fromObjectMetadataEntityToFlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-object-metadata-entity-to-flat-object-metadata.util';
|
||||
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
|
||||
export const fromObjectMetadataEntityToFlatObjectMetadataWithoutFields = (
|
||||
objectMetadataEntity: ObjectMetadataEntity,
|
||||
): FlatObjectMetadataWithoutFields => {
|
||||
return fromObjectMetadataEntityToFlatObjectMetadata({
|
||||
...objectMetadataEntity,
|
||||
fields: [],
|
||||
});
|
||||
};
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import {
|
||||
type CreateFieldAction,
|
||||
type DeleteFieldAction,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-field-action-v2';
|
||||
|
||||
type FlatFieldMetadataAndFlatObjectMetadata = {
|
||||
flatFieldMetadata: FlatFieldMetadata;
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
};
|
||||
|
||||
export const getWorkspaceMigrationV2FieldCreateAction = ({
|
||||
flatFieldMetadata,
|
||||
}: FlatFieldMetadataAndFlatObjectMetadata): CreateFieldAction => ({
|
||||
type: 'create_field',
|
||||
flatFieldMetadata,
|
||||
});
|
||||
|
||||
export const getWorkspaceMigrationV2FieldDeleteAction = ({
|
||||
flatFieldMetadata,
|
||||
flatObjectMetadata,
|
||||
}: FlatFieldMetadataAndFlatObjectMetadata): DeleteFieldAction => ({
|
||||
type: 'delete_field',
|
||||
fieldMetadataId: flatFieldMetadata.id,
|
||||
objectMetadataId: flatObjectMetadata.id,
|
||||
});
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { type CreateFieldAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-field-action-v2';
|
||||
import {
|
||||
type CreateObjectAction,
|
||||
type DeleteObjectAction,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-object-action-v2';
|
||||
import { fromFlatObjectMetadataToFlatObjectMetadataWithoutFields } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/from-flat-object-metadata-to-flat-object-metadata-without-fields.util';
|
||||
|
||||
export const getWorkspaceMigrationV2ObjectCreateAction = ({
|
||||
createFieldActions,
|
||||
flatObjectMetadata,
|
||||
}: {
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
createFieldActions: CreateFieldAction[];
|
||||
}): CreateObjectAction => ({
|
||||
type: 'create_object',
|
||||
flatObjectMetadataWithoutFields:
|
||||
fromFlatObjectMetadataToFlatObjectMetadataWithoutFields(flatObjectMetadata),
|
||||
createFieldActions,
|
||||
});
|
||||
|
||||
export const getWorkspaceMigrationV2ObjectDeleteAction = (
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
): DeleteObjectAction => ({
|
||||
type: 'delete_object',
|
||||
objectMetadataId: flatObjectMetadata.id,
|
||||
});
|
||||
-7
@@ -3,21 +3,14 @@ import { Injectable } from '@nestjs/common';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { FlatEntityMaps } from 'src/engine/core-modules/common/types/flat-entity-maps.type';
|
||||
import { CronTriggerExceptionCode } from 'src/engine/metadata-modules/cron-trigger/exceptions/cron-trigger.exception';
|
||||
import { FlatCronTrigger } from 'src/engine/metadata-modules/cron-trigger/types/flat-cron-trigger.type';
|
||||
import { FailedFlatEntityValidation } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/types/failed-flat-entity-validation.type';
|
||||
|
||||
export type CronTriggerRelatedFlatEntityMaps = Pick<
|
||||
AllFlatEntityMaps,
|
||||
'flatCronTriggerMaps'
|
||||
>;
|
||||
|
||||
type CronTriggerValidationArgs = {
|
||||
flatCronTriggerToValidate: FlatCronTrigger;
|
||||
optimisticFlatCronTriggerMaps: FlatEntityMaps<FlatCronTrigger>;
|
||||
dependencyOptimisticFlatEntityMaps: CronTriggerRelatedFlatEntityMaps;
|
||||
};
|
||||
// TODO: validate settings integrity
|
||||
@Injectable()
|
||||
|
||||
-7
@@ -3,21 +3,14 @@ import { Injectable } from '@nestjs/common';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { FlatEntityMaps } from 'src/engine/core-modules/common/types/flat-entity-maps.type';
|
||||
import { DatabaseEventTriggerExceptionCode } from 'src/engine/metadata-modules/database-event-trigger/exceptions/database-event-trigger.exception';
|
||||
import { FlatDatabaseEventTrigger } from 'src/engine/metadata-modules/database-event-trigger/types/flat-database-event-trigger.type';
|
||||
import { FailedFlatEntityValidation } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/types/failed-flat-entity-validation.type';
|
||||
|
||||
export type DatabaseEventTriggerRelatedFlatEntityMaps = Pick<
|
||||
AllFlatEntityMaps,
|
||||
'flatDatabaseEventTriggerMaps'
|
||||
>;
|
||||
|
||||
type DatabaseEventTriggerValidationArgs = {
|
||||
flatDatabaseEventTriggerToValidate: FlatDatabaseEventTrigger;
|
||||
optimisticFlatDatabaseEventTriggerMaps: FlatEntityMaps<FlatDatabaseEventTrigger>;
|
||||
dependencyOptimisticFlatEntityMaps: DatabaseEventTriggerRelatedFlatEntityMaps;
|
||||
};
|
||||
// TODO: validate settings integrity
|
||||
@Injectable()
|
||||
|
||||
+331
@@ -0,0 +1,331 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { FlatEntityMaps } from 'src/engine/core-modules/common/types/flat-entity-maps.type';
|
||||
import { FieldMetadataExceptionCode } from 'src/engine/metadata-modules/field-metadata/field-metadata.exception';
|
||||
import { FLAT_FIELD_METADATA_RELATION_PROPERTIES_TO_COMPARE } from 'src/engine/metadata-modules/flat-field-metadata/constants/flat-field-metadata-relation-properties-to-compare.constant';
|
||||
import { FlatFieldMetadataTypeValidatorService } from 'src/engine/metadata-modules/flat-field-metadata/services/flat-field-metadata-type-validator.service';
|
||||
import { FlatFieldMetadataRelationPropertiesToCompare } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata-relation-properties-to-compare.type';
|
||||
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { compareTwoFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/compare-two-flat-field-metadata.util';
|
||||
import { isFlatFieldMetadataNameSyncedWithLabel } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-name-synced-with-label.util';
|
||||
import { isMorphOrRelationFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-metadata.util';
|
||||
import { validateFlatFieldMetadataNameAvailability } from 'src/engine/metadata-modules/flat-field-metadata/validators/utils/validate-flat-field-metadata-name-availability.util';
|
||||
import { validateFlatFieldMetadataName } from 'src/engine/metadata-modules/flat-field-metadata/validators/utils/validate-flat-field-metadata-name.util';
|
||||
import { isStandardMetadata } from 'src/engine/metadata-modules/utils/is-standard-metadata.util';
|
||||
import { FailedFlatEntityValidation } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/types/failed-flat-entity-validation.type';
|
||||
|
||||
export type ValidateOneFieldMetadataArgs<
|
||||
T extends FieldMetadataType = FieldMetadataType,
|
||||
> = {
|
||||
dependencyOptimisticFlatEntityMaps: Pick<
|
||||
AllFlatEntityMaps,
|
||||
'flatObjectMetadataMaps'
|
||||
>;
|
||||
remainingFlatEntityMapsToValidate?: FlatEntityMaps<FlatFieldMetadata>;
|
||||
flatFieldMetadataToValidate: FlatFieldMetadata<T>;
|
||||
optimisticFlatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
workspaceId: string;
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class FlatFieldMetadataValidatorService {
|
||||
constructor(
|
||||
private readonly flatFieldMetadataTypeValidatorService: FlatFieldMetadataTypeValidatorService,
|
||||
) {}
|
||||
|
||||
async validateFlatFieldMetadataUpdate<T extends FieldMetadataType>({
|
||||
dependencyOptimisticFlatEntityMaps: { flatObjectMetadataMaps },
|
||||
flatFieldMetadataToValidate,
|
||||
optimisticFlatFieldMetadataMaps,
|
||||
workspaceId,
|
||||
}: ValidateOneFieldMetadataArgs<T>): Promise<
|
||||
FailedFlatEntityValidation<FlatFieldMetadata>
|
||||
> {
|
||||
const validationResult: FailedFlatEntityValidation<FlatFieldMetadata> = {
|
||||
type: 'update_field',
|
||||
errors: [],
|
||||
flatEntityMinimalInformation: {
|
||||
id: flatFieldMetadataToValidate.id,
|
||||
name: flatFieldMetadataToValidate.name,
|
||||
objectMetadataId: flatFieldMetadataToValidate.objectMetadataId,
|
||||
},
|
||||
};
|
||||
const flatObjectMetadata =
|
||||
flatObjectMetadataMaps.byId[flatFieldMetadataToValidate.objectMetadataId];
|
||||
|
||||
if (!isDefined(flatObjectMetadata)) {
|
||||
validationResult.errors.push({
|
||||
code: FieldMetadataExceptionCode.OBJECT_METADATA_NOT_FOUND,
|
||||
message: 'field metadata to update object metadata not found',
|
||||
userFriendlyMessage: t`Object related to field to update not found`,
|
||||
});
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
if (!isDefined(flatObjectMetadata.labelIdentifierFieldMetadataId)) {
|
||||
validationResult.errors.push({
|
||||
code: FieldMetadataExceptionCode.LABEL_IDENTIFIER_FIELD_METADATA_ID_NOT_FOUND,
|
||||
message: 'Label identifier field metadata id does not exist',
|
||||
userFriendlyMessage: t`Object related to updated field does not have a label identifier`,
|
||||
});
|
||||
}
|
||||
|
||||
const existingFlatFieldMetadataToUpdate =
|
||||
optimisticFlatFieldMetadataMaps.byId[flatFieldMetadataToValidate.id];
|
||||
|
||||
if (!isDefined(existingFlatFieldMetadataToUpdate)) {
|
||||
validationResult.errors.push({
|
||||
code: FieldMetadataExceptionCode.FIELD_METADATA_NOT_FOUND,
|
||||
message: 'field metadata to update not found',
|
||||
userFriendlyMessage: t`Field to update not found`,
|
||||
});
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
const updates = compareTwoFlatFieldMetadata({
|
||||
fromFlatFieldMetadata: existingFlatFieldMetadataToUpdate,
|
||||
toFlatFieldMetadata: flatFieldMetadataToValidate,
|
||||
});
|
||||
|
||||
if (isMorphOrRelationFlatFieldMetadata(flatFieldMetadataToValidate)) {
|
||||
const relationNonEditableUpdatedProperties = updates.flatMap(
|
||||
({ property }) =>
|
||||
!FLAT_FIELD_METADATA_RELATION_PROPERTIES_TO_COMPARE.includes(
|
||||
property as FlatFieldMetadataRelationPropertiesToCompare,
|
||||
)
|
||||
? property
|
||||
: [],
|
||||
);
|
||||
|
||||
if (relationNonEditableUpdatedProperties.length > 0) {
|
||||
validationResult.errors.push({
|
||||
code: FieldMetadataExceptionCode.FIELD_MUTATION_NOT_ALLOWED,
|
||||
message: `Forbidden updated properties for relation field metadata: ${relationNonEditableUpdatedProperties.join(', ')}`,
|
||||
userFriendlyMessage: t`Forbidden updated properties for relation field metadata`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (updates.some((update) => update.property === 'name')) {
|
||||
validationResult.errors.push(
|
||||
...validateFlatFieldMetadataName(flatFieldMetadataToValidate.name),
|
||||
...validateFlatFieldMetadataNameAvailability({
|
||||
flatFieldMetadata: flatFieldMetadataToValidate,
|
||||
flatFieldMetadataMaps: optimisticFlatFieldMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
flatFieldMetadataToValidate.isLabelSyncedWithName &&
|
||||
!isFlatFieldMetadataNameSyncedWithLabel(flatFieldMetadataToValidate)
|
||||
) {
|
||||
validationResult.errors.push({
|
||||
code: FieldMetadataExceptionCode.FIELD_MUTATION_NOT_ALLOWED,
|
||||
message: `Name is not synced with label.`,
|
||||
userFriendlyMessage: t`Updated field name is not synced with label`,
|
||||
});
|
||||
}
|
||||
|
||||
const fieldMetadataTypeValidationErrors =
|
||||
await this.flatFieldMetadataTypeValidatorService.validateFlatFieldMetadataTypeSpecificities(
|
||||
{
|
||||
dependencyOptimisticFlatEntityMaps: { flatObjectMetadataMaps },
|
||||
optimisticFlatFieldMetadataMaps,
|
||||
flatFieldMetadataToValidate,
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
if (fieldMetadataTypeValidationErrors.length > 0) {
|
||||
validationResult.errors.push(...fieldMetadataTypeValidationErrors);
|
||||
}
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
validateFlatFieldMetadataDeletion({
|
||||
dependencyOptimisticFlatEntityMaps: { flatObjectMetadataMaps },
|
||||
flatFieldMetadataToValidate: { id: flatFieldMetadataToDeleteId },
|
||||
optimisticFlatFieldMetadataMaps,
|
||||
}: ValidateOneFieldMetadataArgs): FailedFlatEntityValidation<FlatFieldMetadata> {
|
||||
const validationResult: FailedFlatEntityValidation<FlatFieldMetadata> = {
|
||||
type: 'delete_field',
|
||||
errors: [],
|
||||
flatEntityMinimalInformation: {
|
||||
id: flatFieldMetadataToDeleteId,
|
||||
},
|
||||
};
|
||||
|
||||
const flatFieldMetadataToDelete =
|
||||
optimisticFlatFieldMetadataMaps.byId[flatFieldMetadataToDeleteId];
|
||||
|
||||
if (!isDefined(flatFieldMetadataToDelete)) {
|
||||
validationResult.errors.push({
|
||||
code: FieldMetadataExceptionCode.FIELD_METADATA_NOT_FOUND,
|
||||
message: 'Field metadata to delete not found',
|
||||
userFriendlyMessage: t`Field metadata to delete not found`,
|
||||
});
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
validationResult.flatEntityMinimalInformation = {
|
||||
name: flatFieldMetadataToDelete.name,
|
||||
objectMetadataId: flatFieldMetadataToDelete.objectMetadataId,
|
||||
};
|
||||
|
||||
const relatedFlatObjectMetadata =
|
||||
flatObjectMetadataMaps.byId[flatFieldMetadataToDelete.objectMetadataId];
|
||||
|
||||
if (
|
||||
isDefined(relatedFlatObjectMetadata) &&
|
||||
relatedFlatObjectMetadata.labelIdentifierFieldMetadataId ===
|
||||
flatFieldMetadataToDelete.id
|
||||
) {
|
||||
validationResult.errors.push({
|
||||
code: FieldMetadataExceptionCode.FIELD_MUTATION_NOT_ALLOWED,
|
||||
message:
|
||||
'Cannot delete, please update the label identifier field first',
|
||||
userFriendlyMessage: t`Cannot delete, please update the label identifier field first`,
|
||||
});
|
||||
}
|
||||
|
||||
const relationTargetObjectMetadataHasBeenDeleted =
|
||||
isMorphOrRelationFlatFieldMetadata(flatFieldMetadataToDelete) &&
|
||||
!isDefined(
|
||||
flatObjectMetadataMaps.byId[
|
||||
flatFieldMetadataToDelete.relationTargetObjectMetadataId
|
||||
],
|
||||
);
|
||||
const parentObjectMetadataHasBeenDeleted = !isDefined(
|
||||
flatObjectMetadataMaps.byId[flatFieldMetadataToDelete.objectMetadataId],
|
||||
);
|
||||
|
||||
if (
|
||||
isStandardMetadata(flatFieldMetadataToDelete) &&
|
||||
!relationTargetObjectMetadataHasBeenDeleted &&
|
||||
!parentObjectMetadataHasBeenDeleted
|
||||
) {
|
||||
validationResult.errors.push({
|
||||
code: FieldMetadataExceptionCode.INVALID_FIELD_INPUT,
|
||||
message: "Standard Fields can't be deleted",
|
||||
userFriendlyMessage: t`Standard fields cannot be deleted`,
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
flatFieldMetadataToDelete.isActive &&
|
||||
!relationTargetObjectMetadataHasBeenDeleted &&
|
||||
!parentObjectMetadataHasBeenDeleted
|
||||
) {
|
||||
validationResult.errors.push({
|
||||
code: FieldMetadataExceptionCode.INVALID_FIELD_INPUT,
|
||||
message: "Active fields can't be deleted",
|
||||
userFriendlyMessage: t`Active fields cannot be deleted`,
|
||||
});
|
||||
}
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
async validateFlatFieldMetadataCreation<
|
||||
T extends FieldMetadataType = FieldMetadataType,
|
||||
>({
|
||||
dependencyOptimisticFlatEntityMaps: { flatObjectMetadataMaps },
|
||||
flatFieldMetadataToValidate,
|
||||
optimisticFlatFieldMetadataMaps,
|
||||
workspaceId,
|
||||
remainingFlatEntityMapsToValidate,
|
||||
}: ValidateOneFieldMetadataArgs<T>): Promise<
|
||||
FailedFlatEntityValidation<FlatFieldMetadata>
|
||||
> {
|
||||
const validationResult: FailedFlatEntityValidation<FlatFieldMetadata> = {
|
||||
errors: [],
|
||||
flatEntityMinimalInformation: {
|
||||
id: flatFieldMetadataToValidate.id,
|
||||
name: flatFieldMetadataToValidate.name,
|
||||
objectMetadataId: flatFieldMetadataToValidate.objectMetadataId,
|
||||
},
|
||||
type: 'create_field',
|
||||
};
|
||||
|
||||
const parentFlatObjectMetadata =
|
||||
flatObjectMetadataMaps.byId[flatFieldMetadataToValidate.objectMetadataId];
|
||||
|
||||
if (!isDefined(parentFlatObjectMetadata)) {
|
||||
validationResult.errors.push({
|
||||
code: FieldMetadataExceptionCode.OBJECT_METADATA_NOT_FOUND,
|
||||
message: 'Object metadata not found',
|
||||
userFriendlyMessage: t`Field to create related object not found`,
|
||||
});
|
||||
} else {
|
||||
if (
|
||||
isDefined(
|
||||
optimisticFlatFieldMetadataMaps.byId[flatFieldMetadataToValidate.id],
|
||||
)
|
||||
) {
|
||||
validationResult.errors.push({
|
||||
code: FieldMetadataExceptionCode.FIELD_ALREADY_EXISTS,
|
||||
message: 'Field with same id already exists in object',
|
||||
userFriendlyMessage: t`Field already exists`,
|
||||
});
|
||||
}
|
||||
|
||||
if (parentFlatObjectMetadata.isRemote === true) {
|
||||
validationResult.errors.push({
|
||||
code: FieldMetadataExceptionCode.FIELD_MUTATION_NOT_ALLOWED,
|
||||
message: 'Remote objects are read-only',
|
||||
userFriendlyMessage: t`Remote objects are not production ready yet`,
|
||||
});
|
||||
}
|
||||
|
||||
validationResult.errors.push(
|
||||
...validateFlatFieldMetadataNameAvailability({
|
||||
flatFieldMetadata: flatFieldMetadataToValidate,
|
||||
flatFieldMetadataMaps: optimisticFlatFieldMetadataMaps,
|
||||
remainingFlatEntityMapsToValidate,
|
||||
flatObjectMetadata: parentFlatObjectMetadata,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
flatFieldMetadataToValidate.isLabelSyncedWithName &&
|
||||
!isFlatFieldMetadataNameSyncedWithLabel(flatFieldMetadataToValidate)
|
||||
) {
|
||||
validationResult.errors.push({
|
||||
code: FieldMetadataExceptionCode.NAME_NOT_SYNCED_WITH_LABEL,
|
||||
message: `Name is not synced with label`,
|
||||
userFriendlyMessage: t`Field name is not synced with field label`,
|
||||
value: flatFieldMetadataToValidate.label,
|
||||
});
|
||||
}
|
||||
|
||||
validationResult.errors.push(
|
||||
...validateFlatFieldMetadataName(flatFieldMetadataToValidate.name),
|
||||
);
|
||||
|
||||
validationResult.errors.push(
|
||||
...(await this.flatFieldMetadataTypeValidatorService.validateFlatFieldMetadataTypeSpecificities(
|
||||
{
|
||||
dependencyOptimisticFlatEntityMaps: { flatObjectMetadataMaps },
|
||||
flatFieldMetadataToValidate,
|
||||
optimisticFlatFieldMetadataMaps,
|
||||
workspaceId,
|
||||
remainingFlatEntityMapsToValidate,
|
||||
},
|
||||
)),
|
||||
);
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
}
|
||||
+8
-7
@@ -8,7 +8,6 @@ import { FlatEntityMaps } from 'src/engine/core-modules/common/types/flat-entity
|
||||
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/core-modules/common/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
|
||||
import { IndexExceptionCode } from 'src/engine/metadata-modules/flat-index-metadata/exceptions/index-exception-code';
|
||||
import { FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type';
|
||||
import { findFlatFieldMetadataInFlatObjectMetadataMapsWithOnlyFieldId } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-field-metadata-in-flat-object-metadata-maps-with-field-id-only.util';
|
||||
import { IndexRelatedFlatEntityMaps } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/index/workspace-migration-v2-index-actions-builder.service';
|
||||
import { FailedFlatEntityValidation } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/types/failed-flat-entity-validation.type';
|
||||
|
||||
@@ -49,7 +48,10 @@ export class FlatIndexValidatorService {
|
||||
public validateFlatIndexCreation({
|
||||
flatIndexToValidate,
|
||||
optimisticFlatIndexMaps,
|
||||
dependencyOptimisticFlatEntityMaps: { flatObjectMetadataMaps },
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadataMaps,
|
||||
},
|
||||
}: {
|
||||
flatIndexToValidate: FlatIndexMetadata;
|
||||
optimisticFlatIndexMaps: FlatEntityMaps<FlatIndexMetadata>;
|
||||
@@ -116,11 +118,10 @@ export class FlatIndexValidatorService {
|
||||
});
|
||||
} else {
|
||||
flatIndexToValidate.flatIndexFieldMetadatas.forEach((flatIndexField) => {
|
||||
const relatedFlatField =
|
||||
findFlatFieldMetadataInFlatObjectMetadataMapsWithOnlyFieldId({
|
||||
fieldMetadataId: flatIndexField.fieldMetadataId,
|
||||
flatObjectMetadataMaps,
|
||||
});
|
||||
const relatedFlatField = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: flatIndexField.fieldMetadataId,
|
||||
flatEntityMaps: flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(relatedFlatField)) {
|
||||
validationResult.errors.push({
|
||||
|
||||
+253
@@ -0,0 +1,253 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { FlatEntityMaps } from 'src/engine/core-modules/common/types/flat-entity-maps.type';
|
||||
import { FlatObjectMetadataValidationError } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata-validation-error.type';
|
||||
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { areFlatObjectMetadataNamesSyncedWithLabels } from 'src/engine/metadata-modules/flat-object-metadata/utils/are-flat-object-metadata-names-synced-with-labels.util';
|
||||
import { validateFlatObjectMetadataLabel } from 'src/engine/metadata-modules/flat-object-metadata/validators/utils/validate-flat-object-metadata-label.util';
|
||||
import { validateFlatObjectMetadataNames } from 'src/engine/metadata-modules/flat-object-metadata/validators/utils/validate-flat-object-metadata-name.util';
|
||||
import { ObjectMetadataExceptionCode } from 'src/engine/metadata-modules/object-metadata/object-metadata.exception';
|
||||
import { isStandardMetadata } from 'src/engine/metadata-modules/utils/is-standard-metadata.util';
|
||||
import { doesOtherObjectWithSameNameExists } from 'src/engine/metadata-modules/utils/validate-no-other-object-with-same-name-exists-or-throw.util';
|
||||
import { ObjectMetadataRelatedFlatEntityMaps } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/object/services/workspace-migration-v2-object-actions-builder.service';
|
||||
import { FailedFlatEntityValidation } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/types/failed-flat-entity-validation.type';
|
||||
import { WorkspaceMigrationBuilderOptions } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-builder-options.type';
|
||||
|
||||
export type ValidateOneObjectMetadataArgs = {
|
||||
flatObjectMetadataToValidate: FlatObjectMetadata;
|
||||
optimisticFlatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>;
|
||||
buildOptions: WorkspaceMigrationBuilderOptions;
|
||||
dependencyOptimisticFlatEntityMaps: ObjectMetadataRelatedFlatEntityMaps;
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class FlatObjectMetadataValidatorService {
|
||||
public validateFlatObjectMetadataUpdate({
|
||||
optimisticFlatObjectMetadataMaps,
|
||||
flatObjectMetadataToValidate: updatedFlatObjectMetadata,
|
||||
// dependencyOptimisticFlatEntityMaps: { flatFieldMetadataMaps },
|
||||
}: ValidateOneObjectMetadataArgs): FailedFlatEntityValidation<FlatObjectMetadata> {
|
||||
const validationResult: FailedFlatEntityValidation<FlatObjectMetadata> = {
|
||||
type: 'update_object',
|
||||
errors: [],
|
||||
flatEntityMinimalInformation: {
|
||||
id: updatedFlatObjectMetadata.id,
|
||||
},
|
||||
};
|
||||
|
||||
const existingFlatObjectMetadata =
|
||||
optimisticFlatObjectMetadataMaps.byId[updatedFlatObjectMetadata.id];
|
||||
|
||||
if (!isDefined(existingFlatObjectMetadata)) {
|
||||
validationResult.errors.push({
|
||||
code: ObjectMetadataExceptionCode.OBJECT_METADATA_NOT_FOUND,
|
||||
message: t`Object to update not found`,
|
||||
userFriendlyMessage: t`Object to update not found`,
|
||||
});
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
validationResult.flatEntityMinimalInformation = {
|
||||
id: existingFlatObjectMetadata.id,
|
||||
namePlural: existingFlatObjectMetadata.namePlural,
|
||||
nameSingular: existingFlatObjectMetadata.nameSingular,
|
||||
};
|
||||
|
||||
validationResult.errors.push(
|
||||
...this.validateFlatObjectMetadataNameAndLabels({
|
||||
optimisticFlatObjectMetadataMaps,
|
||||
flatObjectMetadataToValidate: updatedFlatObjectMetadata,
|
||||
}),
|
||||
);
|
||||
|
||||
// const { objectFlatFieldMetadatas } =
|
||||
// findObjectFieldsInFlatFieldMetadataMapsOrThrow({
|
||||
// flatFieldMetadataMaps,
|
||||
// flatObjectMetadata: existingFlatObjectMetadata,
|
||||
// });
|
||||
|
||||
// validationResult.errors.push(
|
||||
// ...validateFlatObjectMetadataIdentifiers({
|
||||
// flatObjectMetadata: existingFlatObjectMetadata,
|
||||
// objectFlatFieldMetadatas,
|
||||
// }),
|
||||
// );
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
public validateFlatObjectMetadataDeletion({
|
||||
flatObjectMetadataToValidate: { id: objectMetadataToDeleteId },
|
||||
optimisticFlatObjectMetadataMaps,
|
||||
buildOptions,
|
||||
}: ValidateOneObjectMetadataArgs): FailedFlatEntityValidation<FlatObjectMetadata> {
|
||||
const validationResult: FailedFlatEntityValidation<FlatObjectMetadata> = {
|
||||
type: 'delete_object',
|
||||
errors: [],
|
||||
flatEntityMinimalInformation: {
|
||||
id: objectMetadataToDeleteId,
|
||||
},
|
||||
};
|
||||
|
||||
const flatObjectMetadataToDelete =
|
||||
optimisticFlatObjectMetadataMaps.byId[objectMetadataToDeleteId];
|
||||
|
||||
if (!isDefined(flatObjectMetadataToDelete)) {
|
||||
validationResult.errors.push({
|
||||
code: ObjectMetadataExceptionCode.OBJECT_METADATA_NOT_FOUND,
|
||||
message: t`Object to delete not found`,
|
||||
userFriendlyMessage: t`Object to delete not found`,
|
||||
});
|
||||
} else {
|
||||
validationResult.flatEntityMinimalInformation = {
|
||||
id: flatObjectMetadataToDelete.id,
|
||||
namePlural: flatObjectMetadataToDelete.namePlural,
|
||||
nameSingular: flatObjectMetadataToDelete.nameSingular,
|
||||
};
|
||||
|
||||
if (flatObjectMetadataToDelete.isRemote) {
|
||||
validationResult.errors.push({
|
||||
code: ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
|
||||
message: t`Remote objects are not supported yet`,
|
||||
userFriendlyMessage: t`Remote objects are not supported yet`,
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
!buildOptions.isSystemBuild &&
|
||||
isStandardMetadata(flatObjectMetadataToDelete)
|
||||
) {
|
||||
validationResult.errors.push({
|
||||
code: ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
|
||||
message: t`Standard objects cannot be deleted`,
|
||||
userFriendlyMessage: t`Standard objects cannot be deleted`,
|
||||
});
|
||||
}
|
||||
|
||||
if (!buildOptions.isSystemBuild && flatObjectMetadataToDelete.isActive) {
|
||||
validationResult.errors.push({
|
||||
code: ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
|
||||
message: t`Active objects cannot be deleted`,
|
||||
userFriendlyMessage: t`Active objects cannot be deleted`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
public async validateFlatObjectMetadataCreation({
|
||||
optimisticFlatObjectMetadataMaps,
|
||||
flatObjectMetadataToValidate,
|
||||
// dependencyOptimisticFlatEntityMaps: { flatFieldMetadataMaps },
|
||||
}: ValidateOneObjectMetadataArgs): Promise<
|
||||
FailedFlatEntityValidation<FlatObjectMetadata>
|
||||
> {
|
||||
const objectValidationResult: FailedFlatEntityValidation<FlatObjectMetadata> =
|
||||
{
|
||||
type: 'create_object',
|
||||
errors: [],
|
||||
flatEntityMinimalInformation: {
|
||||
id: flatObjectMetadataToValidate.id,
|
||||
namePlural: flatObjectMetadataToValidate.namePlural,
|
||||
nameSingular: flatObjectMetadataToValidate.nameSingular,
|
||||
},
|
||||
};
|
||||
|
||||
if (
|
||||
isDefined(
|
||||
optimisticFlatObjectMetadataMaps.byId[flatObjectMetadataToValidate.id],
|
||||
)
|
||||
) {
|
||||
objectValidationResult.errors.push({
|
||||
code: ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
|
||||
message: t`Object with same id already exists`,
|
||||
userFriendlyMessage: t`Object with same id already exists`,
|
||||
});
|
||||
}
|
||||
|
||||
if (flatObjectMetadataToValidate.isRemote) {
|
||||
objectValidationResult.errors.push({
|
||||
code: ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
|
||||
message: t`Remote objects are not supported yet`,
|
||||
userFriendlyMessage: t`Remote objects are not supported yet`,
|
||||
});
|
||||
}
|
||||
|
||||
// const { objectFlatFieldMetadatas } =
|
||||
// findObjectFieldsInFlatFieldMetadataMapsOrThrow({
|
||||
// flatFieldMetadataMaps,
|
||||
// flatObjectMetadata: flatObjectMetadataToValidate,
|
||||
// });
|
||||
|
||||
// objectValidationResult.errors.push(
|
||||
// ...validateFlatObjectMetadataIdentifiers({
|
||||
// flatObjectMetadata: flatObjectMetadataToValidate,
|
||||
// objectFlatFieldMetadatas,
|
||||
// }),
|
||||
// );
|
||||
objectValidationResult.errors.push(
|
||||
...this.validateFlatObjectMetadataNameAndLabels({
|
||||
optimisticFlatObjectMetadataMaps,
|
||||
flatObjectMetadataToValidate,
|
||||
}),
|
||||
);
|
||||
|
||||
return objectValidationResult;
|
||||
}
|
||||
|
||||
private validateFlatObjectMetadataNameAndLabels({
|
||||
optimisticFlatObjectMetadataMaps,
|
||||
flatObjectMetadataToValidate,
|
||||
}: {
|
||||
flatObjectMetadataToValidate: FlatObjectMetadata;
|
||||
optimisticFlatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>;
|
||||
}): FlatObjectMetadataValidationError[] {
|
||||
const errors: FlatObjectMetadataValidationError[] = [];
|
||||
|
||||
errors.push(
|
||||
...validateFlatObjectMetadataNames({
|
||||
namePlural: flatObjectMetadataToValidate.namePlural,
|
||||
nameSingular: flatObjectMetadataToValidate.nameSingular,
|
||||
}),
|
||||
);
|
||||
|
||||
errors.push(
|
||||
...validateFlatObjectMetadataLabel({
|
||||
labelPlural: flatObjectMetadataToValidate.labelPlural,
|
||||
labelSingular: flatObjectMetadataToValidate.labelSingular,
|
||||
}),
|
||||
);
|
||||
|
||||
if (
|
||||
flatObjectMetadataToValidate.isLabelSyncedWithName &&
|
||||
!areFlatObjectMetadataNamesSyncedWithLabels(flatObjectMetadataToValidate)
|
||||
) {
|
||||
errors.push({
|
||||
code: ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
|
||||
message: t`Names are not synced with labels`,
|
||||
userFriendlyMessage: t`Names are not synced with labels`,
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
doesOtherObjectWithSameNameExists({
|
||||
objectMetadataNamePlural: flatObjectMetadataToValidate.namePlural,
|
||||
objectMetadataNameSingular: flatObjectMetadataToValidate.nameSingular,
|
||||
objectMetadataMaps: optimisticFlatObjectMetadataMaps,
|
||||
existingObjectMetadataId: flatObjectMetadataToValidate.id,
|
||||
})
|
||||
) {
|
||||
errors.push({
|
||||
code: ObjectMetadataExceptionCode.OBJECT_ALREADY_EXISTS,
|
||||
message: 'Object already exists',
|
||||
userFriendlyMessage: t`Object already exists`,
|
||||
});
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
}
|
||||
-7
@@ -3,21 +3,14 @@ import { Injectable } from '@nestjs/common';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { FlatEntityMaps } from 'src/engine/core-modules/common/types/flat-entity-maps.type';
|
||||
import { ServerlessFunctionExceptionCode } from 'src/engine/metadata-modules/serverless-function/serverless-function.exception';
|
||||
import { FlatServerlessFunction } from 'src/engine/metadata-modules/serverless-function/types/flat-serverless-function.type';
|
||||
import { FailedFlatEntityValidation } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/types/failed-flat-entity-validation.type';
|
||||
|
||||
export type ServerlessFunctionRelatedFlatEntityMaps = Pick<
|
||||
AllFlatEntityMaps,
|
||||
'flatServerlessFunctionMaps'
|
||||
>;
|
||||
|
||||
type ServerlessFunctionValidationArgs = {
|
||||
flatServerlessFunctionToValidate: FlatServerlessFunction;
|
||||
optimisticFlatServerlessFunctionMaps: FlatEntityMaps<FlatServerlessFunction>;
|
||||
dependencyOptimisticFlatEntityMaps: ServerlessFunctionRelatedFlatEntityMaps;
|
||||
};
|
||||
@Injectable()
|
||||
export class FlatServerlessFunctionValidatorService {
|
||||
|
||||
+20
-20
@@ -3,10 +3,10 @@ import { Injectable } from '@nestjs/common';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/core-modules/common/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
|
||||
import { ViewExceptionCode } from 'src/engine/core-modules/view/exceptions/view.exception';
|
||||
import { FlatViewFieldMaps } from 'src/engine/core-modules/view/flat-view/types/flat-view-field-maps.type';
|
||||
import { FlatViewField } from 'src/engine/core-modules/view/flat-view/types/flat-view-field.type';
|
||||
import { findFlatFieldMetadataInFlatObjectMetadataMapsWithOnlyFieldId } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-field-metadata-in-flat-object-metadata-maps-with-field-id-only.util';
|
||||
import { FailedFlatEntityValidation } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/types/failed-flat-entity-validation.type';
|
||||
import { ViewFieldRelatedFlatEntityMaps } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/types/view-field-related-flat-entity-maps.type';
|
||||
|
||||
@@ -86,8 +86,8 @@ export class FlatViewFieldValidatorService {
|
||||
flatViewFieldToValidate,
|
||||
optimisticFlatViewFieldMaps,
|
||||
dependencyOptimisticFlatEntityMaps: {
|
||||
flatObjectMetadataMaps: optimisticFlatObjectMetadataMaps,
|
||||
flatViewMaps: optimisticFlatViewMaps,
|
||||
flatFieldMetadataMaps,
|
||||
},
|
||||
}: {
|
||||
flatViewFieldToValidate: FlatViewField;
|
||||
@@ -96,9 +96,10 @@ export class FlatViewFieldValidatorService {
|
||||
}): Promise<FailedFlatEntityValidation<FlatViewField>> {
|
||||
const errors = [];
|
||||
|
||||
if (
|
||||
isDefined(optimisticFlatViewFieldMaps.byId[flatViewFieldToValidate.id])
|
||||
) {
|
||||
const existingFlatViewField =
|
||||
optimisticFlatViewFieldMaps.byId[flatViewFieldToValidate.id];
|
||||
|
||||
if (isDefined(existingFlatViewField)) {
|
||||
const flatViewFieldId = flatViewFieldToValidate.id;
|
||||
|
||||
errors.push({
|
||||
@@ -108,11 +109,21 @@ export class FlatViewFieldValidatorService {
|
||||
});
|
||||
}
|
||||
|
||||
const relatedFlatFieldMetadata =
|
||||
findFlatFieldMetadataInFlatObjectMetadataMapsWithOnlyFieldId({
|
||||
fieldMetadataId: flatViewFieldToValidate.fieldMetadataId,
|
||||
flatObjectMetadataMaps: optimisticFlatObjectMetadataMaps,
|
||||
const relatedFlatView =
|
||||
optimisticFlatViewMaps.byId[flatViewFieldToValidate.viewId];
|
||||
|
||||
if (!isDefined(relatedFlatView)) {
|
||||
errors.push({
|
||||
code: ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
message: t`View not found`,
|
||||
userFriendlyMessage: t`View not found`,
|
||||
});
|
||||
}
|
||||
|
||||
const relatedFlatFieldMetadata = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: flatViewFieldToValidate.fieldMetadataId,
|
||||
flatEntityMaps: flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(relatedFlatFieldMetadata)) {
|
||||
errors.push({
|
||||
@@ -122,17 +133,6 @@ export class FlatViewFieldValidatorService {
|
||||
});
|
||||
}
|
||||
|
||||
const optimisticFlatView =
|
||||
optimisticFlatViewMaps.byId[flatViewFieldToValidate.viewId];
|
||||
|
||||
if (!isDefined(optimisticFlatView)) {
|
||||
errors.push({
|
||||
code: ViewExceptionCode.INVALID_VIEW_DATA,
|
||||
message: t`View not found`,
|
||||
userFriendlyMessage: t`View not found`,
|
||||
});
|
||||
}
|
||||
|
||||
const allFlatViewFields = Object.values(
|
||||
optimisticFlatViewFieldMaps.byId,
|
||||
).filter(isDefined);
|
||||
|
||||
+20
@@ -1,19 +1,39 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { FlatFieldMetadataValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-field-metadata-validator.service';
|
||||
import { FlatObjectMetadataValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-object-metadata-validator.service';
|
||||
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
||||
import { FlatFieldMetadataTypeValidatorService } from 'src/engine/metadata-modules/flat-field-metadata/services/flat-field-metadata-type-validator.service';
|
||||
import { FlatCronTriggerValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-cron-trigger-validator.service';
|
||||
import { FlatDatabaseEventTriggerValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-database-event-trigger-validator.service';
|
||||
import { FlatIndexValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-index-metadata-validator.service';
|
||||
import { FlatServerlessFunctionValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-serverless-function-validator.service';
|
||||
import { FlatViewFieldValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-view-field-validator.service';
|
||||
import { FlatViewValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-view-validator.service';
|
||||
|
||||
@Module({
|
||||
imports: [FeatureFlagModule],
|
||||
providers: [
|
||||
FlatViewValidatorService,
|
||||
FlatViewFieldValidatorService,
|
||||
FlatIndexValidatorService,
|
||||
FlatFieldMetadataValidatorService,
|
||||
FlatObjectMetadataValidatorService,
|
||||
FlatServerlessFunctionValidatorService,
|
||||
FlatDatabaseEventTriggerValidatorService,
|
||||
FlatCronTriggerValidatorService,
|
||||
FlatFieldMetadataTypeValidatorService,
|
||||
],
|
||||
exports: [
|
||||
FlatViewValidatorService,
|
||||
FlatViewFieldValidatorService,
|
||||
FlatIndexValidatorService,
|
||||
FlatFieldMetadataValidatorService,
|
||||
FlatObjectMetadataValidatorService,
|
||||
FlatServerlessFunctionValidatorService,
|
||||
FlatDatabaseEventTriggerValidatorService,
|
||||
FlatCronTriggerValidatorService,
|
||||
FlatFieldMetadataTypeValidatorService,
|
||||
],
|
||||
})
|
||||
export class WorkspaceMigrationBuilderValidatorsModule {}
|
||||
|
||||
+3
-18
@@ -2,8 +2,6 @@ import { Module } from '@nestjs/common';
|
||||
|
||||
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
||||
import { FlatFieldMetadataTypeValidatorService } from 'src/engine/metadata-modules/flat-field-metadata/services/flat-field-metadata-type-validator.service';
|
||||
import { FlatFieldMetadataValidatorService } from 'src/engine/metadata-modules/flat-field-metadata/services/flat-field-metadata-validator.service';
|
||||
import { FlatObjectMetadataValidatorService } from 'src/engine/metadata-modules/flat-object-metadata/services/flat-object-metadata-validator.service';
|
||||
import { WorkspaceMigrationV2CronTriggerActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/cron-trigger/workspace-migration-v2-cron-trigger-action-builder.service';
|
||||
import { WorkspaceMigrationV2DatabaseEventTriggerActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/database-event-trigger/workspace-migration-v2-database-event-trigger-actions-builder.service';
|
||||
import { WorkspaceMigrationV2FieldActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/field/services/workspace-migration-v2-field-actions-builder.service';
|
||||
@@ -12,40 +10,27 @@ import { WorkspaceMigrationV2ObjectActionsBuilderService } from 'src/engine/work
|
||||
import { WorkspaceMigrationV2ServerlessFunctionActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/serverless-function/workspace-migration-v2-serverless-function-actions-builder.service';
|
||||
import { WorkspaceMigrationV2ViewFieldActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view-field/workspace-migration-v2-view-field-actions-builder.service';
|
||||
import { WorkspaceMigrationV2ViewActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/view/workspace-migration-v2-view-actions-builder.service';
|
||||
import { WorkspaceMigrationBuilderV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/services/workspace-migration-builder-v2.service';
|
||||
import { FlatCronTriggerValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-cron-trigger-validator.service';
|
||||
import { FlatDatabaseEventTriggerValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-database-event-trigger-validator.service';
|
||||
import { FlatServerlessFunctionValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-serverless-function-validator.service';
|
||||
import { FlatViewFieldValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-view-field-validator.service';
|
||||
import { FlatViewValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-view-validator.service';
|
||||
import { WorkspaceMigrationBuilderValidatorsModule } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/workspace-migration-builder-validators.module';
|
||||
|
||||
@Module({
|
||||
imports: [FeatureFlagModule, WorkspaceMigrationBuilderValidatorsModule],
|
||||
providers: [
|
||||
WorkspaceMigrationBuilderV2Service,
|
||||
FlatFieldMetadataValidatorService,
|
||||
FlatFieldMetadataTypeValidatorService,
|
||||
WorkspaceMigrationV2ObjectActionsBuilderService,
|
||||
FlatObjectMetadataValidatorService,
|
||||
WorkspaceMigrationV2FieldActionsBuilderService,
|
||||
WorkspaceMigrationV2ViewActionsBuilderService,
|
||||
WorkspaceMigrationV2ViewFieldActionsBuilderService,
|
||||
WorkspaceMigrationV2IndexActionsBuilderService,
|
||||
WorkspaceMigrationV2ServerlessFunctionActionsBuilderService,
|
||||
WorkspaceMigrationV2DatabaseEventTriggerActionsBuilderService,
|
||||
WorkspaceMigrationV2FieldActionsBuilderService,
|
||||
WorkspaceMigrationV2CronTriggerActionsBuilderService,
|
||||
FlatViewValidatorService,
|
||||
FlatViewFieldValidatorService,
|
||||
FlatServerlessFunctionValidatorService,
|
||||
FlatDatabaseEventTriggerValidatorService,
|
||||
FlatCronTriggerValidatorService,
|
||||
],
|
||||
exports: [
|
||||
WorkspaceMigrationBuilderV2Service,
|
||||
WorkspaceMigrationV2ViewActionsBuilderService,
|
||||
WorkspaceMigrationV2IndexActionsBuilderService,
|
||||
WorkspaceMigrationV2ObjectActionsBuilderService,
|
||||
WorkspaceMigrationV2ViewFieldActionsBuilderService,
|
||||
WorkspaceMigrationV2FieldActionsBuilderService,
|
||||
WorkspaceMigrationV2ServerlessFunctionActionsBuilderService,
|
||||
WorkspaceMigrationV2DatabaseEventTriggerActionsBuilderService,
|
||||
WorkspaceMigrationV2CronTriggerActionsBuilderService,
|
||||
|
||||
+82
-69
@@ -9,10 +9,10 @@ import {
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface';
|
||||
|
||||
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { addFlatEntityToFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/add-flat-entity-to-flat-entity-maps-or-throw.util';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { isMorphOrRelationFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-metadata.util';
|
||||
import { addFlatFieldMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/add-flat-field-metadata-in-flat-object-metadata-maps-or-throw.util';
|
||||
import { findFlatObjectMetadataWithFlatFieldMapsInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-object-metadata-with-flat-field-maps-in-flat-object-metadata-maps-or-throw.util';
|
||||
import { WorkspaceSchemaManagerService } from 'src/engine/twenty-orm/workspace-schema-manager/workspace-schema-manager.service';
|
||||
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
|
||||
import { convertOnDeleteActionToOnDelete } from 'src/engine/workspace-manager/workspace-migration-runner/utils/convert-on-delete-action-to-on-delete.util';
|
||||
@@ -40,15 +40,19 @@ export class CreateFieldActionHandlerService extends WorkspaceMigrationRunnerAct
|
||||
action,
|
||||
allFlatEntityMaps,
|
||||
}: OptimisticallyApplyActionOnAllFlatEntityMapsArgs<CreateFieldAction>): Partial<AllFlatEntityMaps> {
|
||||
const { flatObjectMetadataMaps } = allFlatEntityMaps;
|
||||
const updatedFlatObjectMetadataMaps =
|
||||
addFlatFieldMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatFieldMetadata: action.flatFieldMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
});
|
||||
const { flatFieldMetadataMaps: existingFlatFieldMetadataMaps } =
|
||||
allFlatEntityMaps;
|
||||
const updatedFlatFieldMetadataMaps = action.flatFieldMetadatas.reduce(
|
||||
(flatFieldMetadataMaps, flatFieldMetadata) =>
|
||||
addFlatEntityToFlatEntityMapsOrThrow({
|
||||
flatEntity: flatFieldMetadata,
|
||||
flatEntityMaps: flatFieldMetadataMaps,
|
||||
}),
|
||||
existingFlatFieldMetadataMaps,
|
||||
);
|
||||
|
||||
return {
|
||||
flatObjectMetadataMaps: updatedFlatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps: updatedFlatFieldMetadataMaps,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -61,9 +65,11 @@ export class CreateFieldActionHandlerService extends WorkspaceMigrationRunnerAct
|
||||
FieldMetadataEntity,
|
||||
);
|
||||
|
||||
const { flatFieldMetadata } = action;
|
||||
const { flatFieldMetadatas } = action;
|
||||
|
||||
await fieldMetadataRepository.insert(flatFieldMetadata);
|
||||
for (const flatFieldMetadata of flatFieldMetadatas) {
|
||||
await fieldMetadataRepository.insert(flatFieldMetadata);
|
||||
}
|
||||
}
|
||||
|
||||
async executeForWorkspaceSchema(
|
||||
@@ -75,76 +81,83 @@ export class CreateFieldActionHandlerService extends WorkspaceMigrationRunnerAct
|
||||
allFlatEntityMaps: { flatObjectMetadataMaps },
|
||||
workspaceId,
|
||||
} = context;
|
||||
const { flatFieldMetadata } = action;
|
||||
const { flatFieldMetadatas } = action;
|
||||
|
||||
const flatObjectMetadataWithFlatFieldMaps =
|
||||
findFlatObjectMetadataWithFlatFieldMapsInFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps,
|
||||
objectMetadataId: flatFieldMetadata.objectMetadataId,
|
||||
// TODO prastoin improve doing batchs
|
||||
for (const flatFieldMetadata of flatFieldMetadatas) {
|
||||
const flatObjectMetadata = findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityMaps: flatObjectMetadataMaps,
|
||||
flatEntityId: flatFieldMetadata.objectMetadataId,
|
||||
});
|
||||
|
||||
const { schemaName, tableName } = getWorkspaceSchemaContextForMigration({
|
||||
workspaceId,
|
||||
flatObjectMetadata: flatObjectMetadataWithFlatFieldMaps,
|
||||
});
|
||||
const { schemaName, tableName } = getWorkspaceSchemaContextForMigration({
|
||||
workspaceId,
|
||||
flatObjectMetadata,
|
||||
});
|
||||
|
||||
const enumOperations = collectEnumOperationsForField({
|
||||
flatFieldMetadata: flatFieldMetadata,
|
||||
tableName,
|
||||
operation: EnumOperation.CREATE,
|
||||
});
|
||||
const enumOperations = collectEnumOperationsForField({
|
||||
flatFieldMetadata,
|
||||
tableName,
|
||||
operation: EnumOperation.CREATE,
|
||||
});
|
||||
|
||||
const columnDefinitions = generateColumnDefinitions({
|
||||
flatFieldMetadata: flatFieldMetadata,
|
||||
flatObjectMetadataWithoutFields: flatObjectMetadataWithFlatFieldMaps,
|
||||
});
|
||||
const columnDefinitions = generateColumnDefinitions({
|
||||
flatFieldMetadata,
|
||||
flatObjectMetadata,
|
||||
});
|
||||
|
||||
await executeBatchEnumOperations({
|
||||
enumOperations,
|
||||
queryRunner,
|
||||
schemaName,
|
||||
workspaceSchemaManagerService: this.workspaceSchemaManagerService,
|
||||
});
|
||||
await executeBatchEnumOperations({
|
||||
enumOperations,
|
||||
queryRunner,
|
||||
schemaName,
|
||||
workspaceSchemaManagerService: this.workspaceSchemaManagerService,
|
||||
});
|
||||
|
||||
await this.workspaceSchemaManagerService.columnManager.addColumns({
|
||||
queryRunner,
|
||||
schemaName,
|
||||
tableName,
|
||||
columnDefinitions,
|
||||
});
|
||||
await this.workspaceSchemaManagerService.columnManager.addColumns({
|
||||
queryRunner,
|
||||
schemaName,
|
||||
tableName,
|
||||
columnDefinitions,
|
||||
});
|
||||
|
||||
if (
|
||||
isMorphOrRelationFlatFieldMetadata(flatFieldMetadata) &&
|
||||
flatFieldMetadata.settings.relationType === RelationType.MANY_TO_ONE
|
||||
) {
|
||||
const referencedTableName = computeObjectTargetTable(
|
||||
flatFieldMetadata.flatRelationTargetObjectMetadata,
|
||||
);
|
||||
if (
|
||||
isMorphOrRelationFlatFieldMetadata(flatFieldMetadata) &&
|
||||
flatFieldMetadata.settings.relationType === RelationType.MANY_TO_ONE
|
||||
) {
|
||||
const targetFlatObjectMetadata =
|
||||
findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: flatFieldMetadata.relationTargetObjectMetadataId,
|
||||
flatEntityMaps: flatObjectMetadataMaps,
|
||||
});
|
||||
const referencedTableName = computeObjectTargetTable(
|
||||
targetFlatObjectMetadata,
|
||||
);
|
||||
|
||||
const joinColumnName = flatFieldMetadata.settings.joinColumnName;
|
||||
const joinColumnName = flatFieldMetadata.settings.joinColumnName;
|
||||
|
||||
if (!isDefined(joinColumnName)) {
|
||||
throw new Error(
|
||||
'Join column name is not defined in a MANY_TO_ONE relation',
|
||||
if (!isDefined(joinColumnName)) {
|
||||
throw new Error(
|
||||
'Join column name is not defined in a MANY_TO_ONE relation',
|
||||
);
|
||||
}
|
||||
|
||||
await this.workspaceSchemaManagerService.foreignKeyManager.createForeignKey(
|
||||
{
|
||||
queryRunner,
|
||||
schemaName,
|
||||
foreignKey: {
|
||||
tableName,
|
||||
columnName: joinColumnName,
|
||||
referencedTableName,
|
||||
referencedColumnName: 'id',
|
||||
onDelete:
|
||||
convertOnDeleteActionToOnDelete(
|
||||
flatFieldMetadata.settings.onDelete,
|
||||
) ?? 'CASCADE',
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
await this.workspaceSchemaManagerService.foreignKeyManager.createForeignKey(
|
||||
{
|
||||
queryRunner,
|
||||
schemaName,
|
||||
foreignKey: {
|
||||
tableName,
|
||||
columnName: joinColumnName,
|
||||
referencedTableName,
|
||||
referencedColumnName: 'id',
|
||||
onDelete:
|
||||
convertOnDeleteActionToOnDelete(
|
||||
flatFieldMetadata.settings.onDelete,
|
||||
) ?? 'CASCADE',
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+19
-30
@@ -8,11 +8,9 @@ import {
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface';
|
||||
|
||||
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { deleteFlatEntityFromFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/delete-flat-entity-from-flat-entity-maps-or-throw.util';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { deleteFieldFromFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/delete-field-from-flat-object-metadata-maps-or-throw.util';
|
||||
import { findFlatFieldMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-field-metadata-in-flat-object-metadata-maps-or-throw.util';
|
||||
import { findFlatObjectMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-object-metadata-in-flat-object-metadata-maps-or-throw.util';
|
||||
import { findFlatObjectMetadataWithFlatFieldMapsInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-object-metadata-with-flat-field-maps-in-flat-object-metadata-maps-or-throw.util';
|
||||
import { WorkspaceSchemaManagerService } from 'src/engine/twenty-orm/workspace-schema-manager/workspace-schema-manager.service';
|
||||
import { type DeleteFieldAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-field-action-v2';
|
||||
import { type WorkspaceMigrationActionRunnerArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/types/workspace-migration-action-runner-args.type';
|
||||
@@ -38,18 +36,17 @@ export class DeleteFieldActionHandlerService extends WorkspaceMigrationRunnerAct
|
||||
action,
|
||||
allFlatEntityMaps,
|
||||
}: OptimisticallyApplyActionOnAllFlatEntityMapsArgs<DeleteFieldAction>): Partial<AllFlatEntityMaps> {
|
||||
const { flatObjectMetadataMaps } = allFlatEntityMaps;
|
||||
const { fieldMetadataId, objectMetadataId } = action;
|
||||
const { flatFieldMetadataMaps } = allFlatEntityMaps;
|
||||
const { fieldMetadataId } = action;
|
||||
|
||||
const updatedFlatObjectMetadataMaps =
|
||||
deleteFieldFromFlatObjectMetadataMapsOrThrow({
|
||||
fieldMetadataId,
|
||||
flatObjectMetadataMaps,
|
||||
objectMetadataId,
|
||||
const updatedFlatFieldMetadataMaps =
|
||||
deleteFlatEntityFromFlatEntityMapsOrThrow({
|
||||
entityToDeleteId: fieldMetadataId,
|
||||
flatEntityMaps: flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
return {
|
||||
flatObjectMetadataMaps: updatedFlatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps: updatedFlatFieldMetadataMaps,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -75,37 +72,29 @@ export class DeleteFieldActionHandlerService extends WorkspaceMigrationRunnerAct
|
||||
const {
|
||||
action,
|
||||
queryRunner,
|
||||
allFlatEntityMaps: { flatObjectMetadataMaps },
|
||||
allFlatEntityMaps: { flatObjectMetadataMaps, flatFieldMetadataMaps },
|
||||
workspaceId,
|
||||
} = context;
|
||||
const { objectMetadataId, fieldMetadataId } = action;
|
||||
|
||||
const flatObjectMetadataWithFlatFieldMaps =
|
||||
findFlatObjectMetadataWithFlatFieldMapsInFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps,
|
||||
objectMetadataId,
|
||||
});
|
||||
const flatObjectMetadata = findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityMaps: flatObjectMetadataMaps,
|
||||
flatEntityId: objectMetadataId,
|
||||
});
|
||||
|
||||
const fieldMetadata = findFlatFieldMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps,
|
||||
objectMetadataId,
|
||||
fieldMetadataId,
|
||||
const fieldMetadata = findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityMaps: flatFieldMetadataMaps,
|
||||
flatEntityId: fieldMetadataId,
|
||||
});
|
||||
|
||||
const { schemaName, tableName } = getWorkspaceSchemaContextForMigration({
|
||||
workspaceId,
|
||||
flatObjectMetadata: flatObjectMetadataWithFlatFieldMaps,
|
||||
flatObjectMetadata,
|
||||
});
|
||||
|
||||
const flatObjectMetadata =
|
||||
findFlatObjectMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps,
|
||||
objectMetadataId,
|
||||
});
|
||||
|
||||
const columnDefinitions = generateColumnDefinitions({
|
||||
flatFieldMetadata: fieldMetadata,
|
||||
flatObjectMetadataWithoutFields: flatObjectMetadata,
|
||||
flatObjectMetadata: flatObjectMetadata,
|
||||
});
|
||||
const columnNamesToDrop = columnDefinitions.map((def) => def.name);
|
||||
|
||||
|
||||
+29
-38
@@ -10,22 +10,19 @@ import {
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface';
|
||||
|
||||
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { replaceFlatEntityInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/replace-flat-entity-in-flat-entity-maps-or-throw.util';
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { MorphOrRelationFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/types/morph-or-relation-field-metadata-type.type';
|
||||
import { computeCompositeColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util';
|
||||
import { getCompositeTypeOrThrow } from 'src/engine/metadata-modules/field-metadata/utils/get-composite-type-or-throw.util';
|
||||
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||
import { FlatFieldMetadataPropertiesToCompare } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata-properties-to-compare.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { isCompositeFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-composite-flat-field-metadata.util';
|
||||
import { isEnumFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-enum-flat-field-metadata.util';
|
||||
import { isMorphOrRelationFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-metadata.util';
|
||||
import { findFlatFieldMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-field-metadata-in-flat-object-metadata-maps-or-throw.util';
|
||||
import { findFlatFieldMetadataInFlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-field-metadata-in-flat-object-metadata-maps.util';
|
||||
import { findFlatObjectMetadataWithFlatFieldMapsInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-object-metadata-with-flat-field-maps-in-flat-object-metadata-maps-or-throw.util';
|
||||
import { replaceFlatFieldMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/replace-flat-field-metadata-in-flat-object-metadata-maps-or-throw.util';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { fromFlatObjectMetadataWithFlatFieldMapsToFlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-flat-object-metadata-with-flat-field-maps-to-flat-object-metadatas.util';
|
||||
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { fieldMetadataTypeToColumnType } from 'src/engine/metadata-modules/workspace-migration/utils/field-metadata-type-to-column-type.util';
|
||||
import { WorkspaceSchemaManagerService } from 'src/engine/twenty-orm/workspace-schema-manager/workspace-schema-manager.service';
|
||||
import { isMorphOrRelationFieldMetadataType } from 'src/engine/utils/is-morph-or-relation-field-metadata-type.util';
|
||||
@@ -71,15 +68,15 @@ export class UpdateFieldActionHandlerService extends WorkspaceMigrationRunnerAct
|
||||
action,
|
||||
allFlatEntityMaps,
|
||||
}: OptimisticallyApplyActionOnAllFlatEntityMapsArgs<UpdateFieldAction>): Partial<AllFlatEntityMaps> {
|
||||
const { flatObjectMetadataMaps } = allFlatEntityMaps;
|
||||
const { fieldMetadataId, objectMetadataId } = action;
|
||||
const { flatFieldMetadataMaps } = allFlatEntityMaps;
|
||||
const { fieldMetadataId } = action;
|
||||
|
||||
const existingFlatFieldMetadata =
|
||||
findFlatFieldMetadataInFlatObjectMetadataMaps({
|
||||
fieldMetadataId,
|
||||
objectMetadataId,
|
||||
flatObjectMetadataMaps,
|
||||
});
|
||||
const existingFlatFieldMetadata = findFlatEntityByIdInFlatEntityMapsOrThrow(
|
||||
{
|
||||
flatEntityId: fieldMetadataId,
|
||||
flatEntityMaps: flatFieldMetadataMaps,
|
||||
},
|
||||
);
|
||||
|
||||
if (!isDefined(existingFlatFieldMetadata)) {
|
||||
throw new WorkspaceMigrationRunnerException(
|
||||
@@ -93,14 +90,14 @@ export class UpdateFieldActionHandlerService extends WorkspaceMigrationRunnerAct
|
||||
...fromWorkspaceMigrationUpdateActionToPartialEntity(action),
|
||||
};
|
||||
|
||||
const updatedFlatObjectMetadataMaps =
|
||||
replaceFlatFieldMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatFieldMetadata: updatedFlatFieldMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
const updatedFlatFieldMetadataMaps =
|
||||
replaceFlatEntityInFlatEntityMapsOrThrow({
|
||||
flatEntity: updatedFlatFieldMetadata,
|
||||
flatEntityMaps: flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
return {
|
||||
flatObjectMetadataMaps: updatedFlatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps: updatedFlatFieldMetadataMaps,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -127,28 +124,25 @@ export class UpdateFieldActionHandlerService extends WorkspaceMigrationRunnerAct
|
||||
const {
|
||||
action,
|
||||
queryRunner,
|
||||
allFlatEntityMaps: { flatObjectMetadataMaps },
|
||||
allFlatEntityMaps: { flatObjectMetadataMaps, flatFieldMetadataMaps },
|
||||
workspaceId,
|
||||
} = context;
|
||||
const { objectMetadataId, fieldMetadataId, updates } = action;
|
||||
|
||||
const flatObjectMetadataWithFlatFieldMaps =
|
||||
findFlatObjectMetadataWithFlatFieldMapsInFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps,
|
||||
objectMetadataId,
|
||||
});
|
||||
const flatObjectMetadata = findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: objectMetadataId,
|
||||
flatEntityMaps: flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
const { schemaName, tableName } = getWorkspaceSchemaContextForMigration({
|
||||
workspaceId,
|
||||
flatObjectMetadata: flatObjectMetadataWithFlatFieldMaps,
|
||||
flatObjectMetadata,
|
||||
});
|
||||
|
||||
const currentFlatFieldMetadata =
|
||||
findFlatFieldMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps,
|
||||
objectMetadataId,
|
||||
fieldMetadataId,
|
||||
});
|
||||
const currentFlatFieldMetadata = findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: fieldMetadataId,
|
||||
flatEntityMaps: flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
let optimisticFlatFieldMetadata = structuredClone(currentFlatFieldMetadata);
|
||||
|
||||
@@ -181,10 +175,7 @@ export class UpdateFieldActionHandlerService extends WorkspaceMigrationRunnerAct
|
||||
queryRunner,
|
||||
schemaName,
|
||||
tableName,
|
||||
flatObjectMetadata:
|
||||
fromFlatObjectMetadataWithFlatFieldMapsToFlatObjectMetadata(
|
||||
flatObjectMetadataWithFlatFieldMaps,
|
||||
),
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadata: optimisticFlatFieldMetadata,
|
||||
update,
|
||||
});
|
||||
@@ -378,7 +369,7 @@ export class UpdateFieldActionHandlerService extends WorkspaceMigrationRunnerAct
|
||||
|
||||
const enumColumnDefinitions = generateColumnDefinitions({
|
||||
flatFieldMetadata: flatFieldMetadata,
|
||||
flatObjectMetadataWithoutFields: flatObjectMetadata,
|
||||
flatObjectMetadata: flatObjectMetadata,
|
||||
});
|
||||
|
||||
for (const enumColumnDefinition of enumColumnDefinitions) {
|
||||
|
||||
+10
-13
@@ -17,9 +17,8 @@ import {
|
||||
} from 'src/engine/core-modules/common/exceptions/flat-entity-maps.exception';
|
||||
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { addFlatEntityToFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/add-flat-entity-to-flat-entity-maps-or-throw.util';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { isMorphOrRelationFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-metadata.util';
|
||||
import { findFlatFieldMetadataInFlatObjectMetadataMapsWithOnlyFieldId } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-field-metadata-in-flat-object-metadata-maps-with-field-id-only.util';
|
||||
import { findFlatObjectMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-object-metadata-in-flat-object-metadata-maps-or-throw.util';
|
||||
import { IndexFieldMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-field-metadata.entity';
|
||||
import { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity';
|
||||
import { WorkspaceSchemaManagerService } from 'src/engine/twenty-orm/workspace-schema-manager/workspace-schema-manager.service';
|
||||
@@ -91,17 +90,16 @@ export class CreateIndexActionHandlerService extends WorkspaceMigrationRunnerAct
|
||||
context: WorkspaceMigrationActionRunnerArgs<CreateIndexAction>,
|
||||
): Promise<void> {
|
||||
const {
|
||||
allFlatEntityMaps: { flatObjectMetadataMaps },
|
||||
allFlatEntityMaps: { flatObjectMetadataMaps, flatFieldMetadataMaps },
|
||||
action: { flatIndexMetadata },
|
||||
queryRunner,
|
||||
workspaceId,
|
||||
} = context;
|
||||
|
||||
const flatObjectMetadata =
|
||||
findFlatObjectMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps,
|
||||
objectMetadataId: flatIndexMetadata.objectMetadataId,
|
||||
});
|
||||
const flatObjectMetadata = findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityMaps: flatObjectMetadataMaps,
|
||||
flatEntityId: flatIndexMetadata.objectMetadataId,
|
||||
});
|
||||
const { schemaName, tableName } = getWorkspaceSchemaContextForMigration({
|
||||
workspaceId,
|
||||
flatObjectMetadata,
|
||||
@@ -109,11 +107,10 @@ export class CreateIndexActionHandlerService extends WorkspaceMigrationRunnerAct
|
||||
|
||||
const quotedColumns = flatIndexMetadata.flatIndexFieldMetadatas.map(
|
||||
({ fieldMetadataId }) => {
|
||||
const flatFieldMetadata =
|
||||
findFlatFieldMetadataInFlatObjectMetadataMapsWithOnlyFieldId({
|
||||
fieldMetadataId,
|
||||
flatObjectMetadataMaps,
|
||||
});
|
||||
const flatFieldMetadata = findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: fieldMetadataId,
|
||||
flatEntityMaps: flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(flatFieldMetadata)) {
|
||||
throw new FlatEntityMapsException(
|
||||
|
||||
+22
-26
@@ -6,11 +6,11 @@ import {
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface';
|
||||
|
||||
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { addFlatEntityToFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/add-flat-entity-to-flat-entity-maps-or-throw.util';
|
||||
import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity';
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { isCompositeFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-composite-flat-field-metadata.util';
|
||||
import { isEnumFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-enum-flat-field-metadata.util';
|
||||
import { addFlatObjectMetadataToFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/add-flat-object-metadata-to-flat-object-metadata-maps-or-throw.util';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { WorkspaceSchemaManagerService } from 'src/engine/twenty-orm/workspace-schema-manager/workspace-schema-manager.service';
|
||||
import { type CreateObjectAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-object-action-v2';
|
||||
@@ -37,24 +37,26 @@ export class CreateObjectActionHandlerService extends WorkspaceMigrationRunnerAc
|
||||
action,
|
||||
allFlatEntityMaps,
|
||||
}: OptimisticallyApplyActionOnAllFlatEntityMapsArgs<CreateObjectAction>): Partial<AllFlatEntityMaps> {
|
||||
const { flatObjectMetadataMaps } = allFlatEntityMaps;
|
||||
const { flatObjectMetadataWithoutFields, createFieldActions } = action;
|
||||
const { flatObjectMetadataMaps, flatFieldMetadataMaps } = allFlatEntityMaps;
|
||||
const { flatObjectMetadata, flatFieldMetadatas } = action;
|
||||
|
||||
const flatFieldMetadatas = createFieldActions.map(
|
||||
(createFieldAction) => createFieldAction.flatFieldMetadata,
|
||||
const updatedFlatFieldMetadatas = flatFieldMetadatas.reduce(
|
||||
(flatFieldMaps, flatFieldMetadata) =>
|
||||
addFlatEntityToFlatEntityMapsOrThrow({
|
||||
flatEntity: flatFieldMetadata,
|
||||
flatEntityMaps: flatFieldMaps,
|
||||
}),
|
||||
flatFieldMetadataMaps,
|
||||
);
|
||||
|
||||
const updatedFlatObjectMetadataMaps =
|
||||
addFlatObjectMetadataToFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadata: {
|
||||
...flatObjectMetadataWithoutFields,
|
||||
flatFieldMetadatas,
|
||||
},
|
||||
flatObjectMetadataMaps,
|
||||
});
|
||||
const updatedFlatObjectMetadataMaps = addFlatEntityToFlatEntityMapsOrThrow({
|
||||
flatEntity: flatObjectMetadata,
|
||||
flatEntityMaps: flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
return {
|
||||
flatObjectMetadataMaps: updatedFlatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps: updatedFlatFieldMetadatas,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -62,7 +64,7 @@ export class CreateObjectActionHandlerService extends WorkspaceMigrationRunnerAc
|
||||
context: WorkspaceMigrationActionRunnerArgs<CreateObjectAction>,
|
||||
): Promise<void> {
|
||||
const { action, queryRunner } = context;
|
||||
const { flatObjectMetadataWithoutFields, createFieldActions } = action;
|
||||
const { flatObjectMetadata, flatFieldMetadatas } = action;
|
||||
|
||||
const objectMetadataRepository =
|
||||
queryRunner.manager.getRepository<ObjectMetadataEntity>(
|
||||
@@ -74,13 +76,13 @@ export class CreateObjectActionHandlerService extends WorkspaceMigrationRunnerAc
|
||||
|
||||
const lastDataSourceMetadata = await dataSourceRepository.findOneOrFail({
|
||||
where: {
|
||||
workspaceId: flatObjectMetadataWithoutFields.workspaceId,
|
||||
workspaceId: flatObjectMetadata.workspaceId,
|
||||
},
|
||||
order: { createdAt: 'DESC' },
|
||||
});
|
||||
|
||||
await objectMetadataRepository.insert({
|
||||
...flatObjectMetadataWithoutFields,
|
||||
...flatObjectMetadata,
|
||||
dataSourceId: lastDataSourceMetadata.id,
|
||||
targetTableName: 'DEPRECATED',
|
||||
});
|
||||
@@ -90,9 +92,7 @@ export class CreateObjectActionHandlerService extends WorkspaceMigrationRunnerAc
|
||||
FieldMetadataEntity,
|
||||
);
|
||||
|
||||
for (const createFieldAction of createFieldActions) {
|
||||
const { flatFieldMetadata } = createFieldAction;
|
||||
|
||||
for (const flatFieldMetadata of flatFieldMetadatas) {
|
||||
await fieldMetadataRepository.save(flatFieldMetadata);
|
||||
}
|
||||
}
|
||||
@@ -101,21 +101,17 @@ export class CreateObjectActionHandlerService extends WorkspaceMigrationRunnerAc
|
||||
context: WorkspaceMigrationActionRunnerArgs<CreateObjectAction>,
|
||||
): Promise<void> {
|
||||
const { action, queryRunner, workspaceId } = context;
|
||||
const { flatObjectMetadataWithoutFields, createFieldActions } = action;
|
||||
const { flatObjectMetadata, flatFieldMetadatas } = action;
|
||||
|
||||
const { schemaName, tableName } = getWorkspaceSchemaContextForMigration({
|
||||
workspaceId,
|
||||
flatObjectMetadata: flatObjectMetadataWithoutFields,
|
||||
flatObjectMetadata,
|
||||
});
|
||||
|
||||
const flatFieldMetadatas = createFieldActions.map(
|
||||
(createFieldAction) => createFieldAction.flatFieldMetadata,
|
||||
);
|
||||
|
||||
const columnDefinitions = flatFieldMetadatas.flatMap((flatFieldMetadata) =>
|
||||
generateColumnDefinitions({
|
||||
flatFieldMetadata,
|
||||
flatObjectMetadataWithoutFields,
|
||||
flatObjectMetadata,
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
+21
-22
@@ -6,11 +6,11 @@ import {
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface';
|
||||
|
||||
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { deleteFlatEntityFromFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/delete-flat-entity-from-flat-entity-maps-or-throw.util';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { findObjectFieldsInFlatFieldMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-object-fields-in-flat-field-metadata-maps-or-throw.util';
|
||||
import { isCompositeFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-composite-flat-field-metadata.util';
|
||||
import { isEnumFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-enum-flat-field-metadata.util';
|
||||
import { deleteObjectFromFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/delete-object-from-flat-object-metadata-maps-or-throw.util';
|
||||
import { findFlatObjectMetadataWithFlatFieldMapsInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-object-metadata-with-flat-field-maps-in-flat-object-metadata-maps-or-throw.util';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { WorkspaceSchemaManagerService } from 'src/engine/twenty-orm/workspace-schema-manager/workspace-schema-manager.service';
|
||||
import { type DeleteObjectAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-object-action-v2';
|
||||
@@ -40,9 +40,9 @@ export class DeleteObjectActionHandlerService extends WorkspaceMigrationRunnerAc
|
||||
const { objectMetadataId } = action;
|
||||
|
||||
const updatedFlatObjectMetadataMaps =
|
||||
deleteObjectFromFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps,
|
||||
objectMetadataId,
|
||||
deleteFlatEntityFromFlatEntityMapsOrThrow({
|
||||
flatEntityMaps: flatObjectMetadataMaps,
|
||||
entityToDeleteId: objectMetadataId,
|
||||
});
|
||||
|
||||
return {
|
||||
@@ -70,20 +70,19 @@ export class DeleteObjectActionHandlerService extends WorkspaceMigrationRunnerAc
|
||||
const {
|
||||
action,
|
||||
queryRunner,
|
||||
allFlatEntityMaps: { flatObjectMetadataMaps },
|
||||
allFlatEntityMaps: { flatObjectMetadataMaps, flatFieldMetadataMaps },
|
||||
workspaceId,
|
||||
} = context;
|
||||
const { objectMetadataId } = action;
|
||||
|
||||
const flatObjectMetadataWithFlatFieldMaps =
|
||||
findFlatObjectMetadataWithFlatFieldMapsInFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps,
|
||||
objectMetadataId,
|
||||
});
|
||||
const flatObjectMetadata = findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityMaps: flatObjectMetadataMaps,
|
||||
flatEntityId: objectMetadataId,
|
||||
});
|
||||
|
||||
const { schemaName, tableName } = getWorkspaceSchemaContextForMigration({
|
||||
workspaceId,
|
||||
flatObjectMetadata: flatObjectMetadataWithFlatFieldMaps,
|
||||
flatObjectMetadata,
|
||||
});
|
||||
|
||||
await this.workspaceSchemaManagerService.tableManager.dropTable({
|
||||
@@ -91,15 +90,15 @@ export class DeleteObjectActionHandlerService extends WorkspaceMigrationRunnerAc
|
||||
schemaName,
|
||||
tableName,
|
||||
});
|
||||
|
||||
const enumOrCompositeFlatFieldMetadatas = Object.values(
|
||||
flatObjectMetadataWithFlatFieldMaps.fieldsById,
|
||||
)
|
||||
.filter((field): field is FlatFieldMetadata => field != null)
|
||||
.filter(
|
||||
(field) =>
|
||||
isEnumFlatFieldMetadata(field) || isCompositeFlatFieldMetadata(field),
|
||||
);
|
||||
const { objectFlatFieldMetadatas } =
|
||||
findObjectFieldsInFlatFieldMetadataMapsOrThrow({
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata,
|
||||
});
|
||||
const enumOrCompositeFlatFieldMetadatas = objectFlatFieldMetadatas.filter(
|
||||
(field) =>
|
||||
isEnumFlatFieldMetadata(field) || isCompositeFlatFieldMetadata(field),
|
||||
);
|
||||
|
||||
const enumOperations = collectEnumOperationsForObject({
|
||||
flatFieldMetadatas: enumOrCompositeFlatFieldMetadatas,
|
||||
|
||||
+26
-26
@@ -6,12 +6,11 @@ import {
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/interfaces/workspace-migration-runner-action-handler-service.interface';
|
||||
|
||||
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { replaceFlatEntityInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/replace-flat-entity-in-flat-entity-maps-or-throw.util';
|
||||
import { findObjectFieldsInFlatFieldMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-object-fields-in-flat-field-metadata-maps-or-throw.util';
|
||||
import { isCompositeFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-composite-flat-field-metadata.util';
|
||||
import { isEnumFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-enum-flat-field-metadata.util';
|
||||
import { findFlatObjectMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-object-metadata-in-flat-object-metadata-maps-or-throw.util';
|
||||
import { findFlatObjectMetadataWithFlatFieldMapsInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-object-metadata-with-flat-field-maps-in-flat-object-metadata-maps-or-throw.util';
|
||||
import { replaceFlatObjectMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/replace-flat-object-metadata-in-flat-object-metadata-maps-or-throw.util';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { WorkspaceSchemaManagerService } from 'src/engine/twenty-orm/workspace-schema-manager/workspace-schema-manager.service';
|
||||
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
|
||||
@@ -43,9 +42,9 @@ export class UpdateObjectActionHandlerService extends WorkspaceMigrationRunnerAc
|
||||
const { objectMetadataId } = action;
|
||||
|
||||
const existingFlatObjectMetadata =
|
||||
findFlatObjectMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
objectMetadataId,
|
||||
flatObjectMetadataMaps,
|
||||
findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: objectMetadataId,
|
||||
flatEntityMaps: flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
const updatedFlatObjectMetadata = {
|
||||
@@ -54,9 +53,9 @@ export class UpdateObjectActionHandlerService extends WorkspaceMigrationRunnerAc
|
||||
};
|
||||
|
||||
const updatedFlatObjectMetadataMaps =
|
||||
replaceFlatObjectMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadata: updatedFlatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
replaceFlatEntityInFlatEntityMapsOrThrow({
|
||||
flatEntity: updatedFlatObjectMetadata,
|
||||
flatEntityMaps: flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
return {
|
||||
@@ -86,21 +85,20 @@ export class UpdateObjectActionHandlerService extends WorkspaceMigrationRunnerAc
|
||||
const {
|
||||
action,
|
||||
queryRunner,
|
||||
allFlatEntityMaps: { flatObjectMetadataMaps },
|
||||
allFlatEntityMaps: { flatObjectMetadataMaps, flatFieldMetadataMaps },
|
||||
workspaceId,
|
||||
} = context;
|
||||
const { objectMetadataId, updates } = action;
|
||||
|
||||
const flatObjectMetadataWithFlatFieldMaps =
|
||||
findFlatObjectMetadataWithFlatFieldMapsInFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps,
|
||||
objectMetadataId,
|
||||
});
|
||||
const flatObjectMetadata = findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityMaps: flatObjectMetadataMaps,
|
||||
flatEntityId: objectMetadataId,
|
||||
});
|
||||
|
||||
const { schemaName, tableName: currentTableName } =
|
||||
getWorkspaceSchemaContextForMigration({
|
||||
workspaceId,
|
||||
flatObjectMetadata: flatObjectMetadataWithFlatFieldMaps,
|
||||
flatObjectMetadata: flatObjectMetadata,
|
||||
});
|
||||
|
||||
for (const update of updates) {
|
||||
@@ -109,7 +107,7 @@ export class UpdateObjectActionHandlerService extends WorkspaceMigrationRunnerAc
|
||||
}
|
||||
|
||||
const updatedObjectMetadata = {
|
||||
...flatObjectMetadataWithFlatFieldMaps,
|
||||
...flatObjectMetadata,
|
||||
[update.property]: update.to,
|
||||
};
|
||||
|
||||
@@ -123,14 +121,16 @@ export class UpdateObjectActionHandlerService extends WorkspaceMigrationRunnerAc
|
||||
newTableName,
|
||||
});
|
||||
|
||||
const enumOrCompositeFlatFieldMetadatas = Object.values(
|
||||
flatObjectMetadataWithFlatFieldMaps.fieldsById,
|
||||
)
|
||||
.filter((field): field is FlatFieldMetadata => field != null)
|
||||
.filter(
|
||||
(field) =>
|
||||
isEnumFlatFieldMetadata(field) ||
|
||||
isCompositeFlatFieldMetadata(field),
|
||||
const { objectFlatFieldMetadatas } =
|
||||
findObjectFieldsInFlatFieldMetadataMapsOrThrow({
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata: updatedObjectMetadata,
|
||||
});
|
||||
const enumOrCompositeFlatFieldMetadatas =
|
||||
objectFlatFieldMetadatas.filter(
|
||||
(flatField) =>
|
||||
isEnumFlatFieldMetadata(flatField) ||
|
||||
isCompositeFlatFieldMetadata(flatField),
|
||||
);
|
||||
|
||||
const enumOperations = collectEnumOperationsForObject({
|
||||
|
||||
+39
-10
@@ -1,5 +1,6 @@
|
||||
import { SetMetadata } from '@nestjs/common';
|
||||
import { Inject, SetMetadata } from '@nestjs/common';
|
||||
|
||||
import { LoggerService } from 'src/engine/core-modules/logger/logger.service';
|
||||
import { type AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import {
|
||||
type ExtractAction,
|
||||
@@ -32,16 +33,19 @@ export abstract class BaseWorkspaceMigrationRunnerActionHandlerService<
|
||||
TActionType extends WorkspaceMigrationActionTypeV2,
|
||||
> implements WorkspaceMigrationRunnerActionHandlerService<TActionType>
|
||||
{
|
||||
public actionType: TActionType;
|
||||
|
||||
@Inject(LoggerService)
|
||||
protected readonly logger: LoggerService;
|
||||
|
||||
executeForMetadata(
|
||||
// eslint-disable-next-line unused-imports/no-unused-vars
|
||||
context: WorkspaceMigrationActionRunnerArgs<ExtractAction<TActionType>>,
|
||||
_context: WorkspaceMigrationActionRunnerArgs<ExtractAction<TActionType>>,
|
||||
): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
executeForWorkspaceSchema(
|
||||
// eslint-disable-next-line unused-imports/no-unused-vars
|
||||
context: WorkspaceMigrationActionRunnerArgs<ExtractAction<TActionType>>,
|
||||
_context: WorkspaceMigrationActionRunnerArgs<ExtractAction<TActionType>>,
|
||||
): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
@@ -55,8 +59,7 @@ export abstract class BaseWorkspaceMigrationRunnerActionHandlerService<
|
||||
}
|
||||
|
||||
rollbackForMetadata(
|
||||
// eslint-disable-next-line unused-imports/no-unused-vars
|
||||
context: WorkspaceMigrationActionRunnerArgs<ExtractAction<TActionType>>,
|
||||
_context: WorkspaceMigrationActionRunnerArgs<ExtractAction<TActionType>>,
|
||||
): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
@@ -65,8 +68,14 @@ export abstract class BaseWorkspaceMigrationRunnerActionHandlerService<
|
||||
context: WorkspaceMigrationActionRunnerArgs<ExtractAction<TActionType>>,
|
||||
): Promise<Partial<AllFlatEntityMaps>> {
|
||||
await Promise.all([
|
||||
this.executeForMetadata(context),
|
||||
this.executeForWorkspaceSchema(context),
|
||||
this.asyncMethodPerformanceMetricWrapper({
|
||||
label: 'executeForMetadata',
|
||||
method: async () => this.executeForMetadata(context),
|
||||
}),
|
||||
this.asyncMethodPerformanceMetricWrapper({
|
||||
label: 'executeForWorkspaceSchema',
|
||||
method: async () => this.executeForWorkspaceSchema(context),
|
||||
}),
|
||||
]);
|
||||
|
||||
return this.optimisticallyApplyActionOnAllFlatEntityMaps({
|
||||
@@ -80,6 +89,24 @@ export abstract class BaseWorkspaceMigrationRunnerActionHandlerService<
|
||||
): Promise<void> {
|
||||
await this.rollbackForMetadata(context);
|
||||
}
|
||||
|
||||
private async asyncMethodPerformanceMetricWrapper({
|
||||
label,
|
||||
method,
|
||||
}: {
|
||||
label: string;
|
||||
method: () => Promise<void>;
|
||||
}): Promise<void> {
|
||||
this.logger.time(
|
||||
'BaseWorkspaceMigrationRunnerActionHandlerService',
|
||||
`${this.actionType} ${label}`,
|
||||
);
|
||||
await method();
|
||||
this.logger.timeEnd(
|
||||
'BaseWorkspaceMigrationRunnerActionHandlerService',
|
||||
`${this.actionType} ${label}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function WorkspaceMigrationRunnerActionHandler<
|
||||
@@ -87,7 +114,9 @@ export function WorkspaceMigrationRunnerActionHandler<
|
||||
>(
|
||||
actionType: TActionType,
|
||||
): typeof BaseWorkspaceMigrationRunnerActionHandlerService<TActionType> {
|
||||
abstract class ActionHandlerService extends BaseWorkspaceMigrationRunnerActionHandlerService<TActionType> {}
|
||||
abstract class ActionHandlerService extends BaseWorkspaceMigrationRunnerActionHandlerService<TActionType> {
|
||||
actionType = actionType;
|
||||
}
|
||||
|
||||
SetMetadata(
|
||||
WORKSPACE_MIGRATION_ACTION_HANDLER_METADATA_KEY,
|
||||
|
||||
+49
-6
@@ -3,12 +3,15 @@ import { InjectDataSource } from '@nestjs/typeorm';
|
||||
|
||||
import { DataSource } from 'typeorm';
|
||||
|
||||
import { LoggerService } from 'src/engine/core-modules/logger/logger.service';
|
||||
import {
|
||||
WorkspaceQueryRunnerException,
|
||||
WorkspaceQueryRunnerExceptionCode,
|
||||
} from 'src/engine/api/graphql/workspace-query-runner/workspace-query-runner.exception';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/core-modules/common/services/workspace-many-or-all-flat-entity-maps-cache.service.';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/core-modules/common/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
||||
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { WorkspaceMetadataVersionService } from 'src/engine/metadata-modules/workspace-metadata-version/services/workspace-metadata-version.service';
|
||||
import { WorkspacePermissionsCacheService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service';
|
||||
import { WorkspaceMigrationV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-v2';
|
||||
import { WorkspaceMigrationRunnerActionHandlerRegistryService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/registry/workspace-migration-runner-action-handler-registry.service';
|
||||
|
||||
@@ -19,6 +22,9 @@ export class WorkspaceMigrationRunnerV2Service {
|
||||
@InjectDataSource()
|
||||
private readonly coreDataSource: DataSource,
|
||||
private readonly workspaceMigrationRunnerActionHandlerRegistry: WorkspaceMigrationRunnerActionHandlerRegistryService,
|
||||
private readonly workspaceMetadataVersionService: WorkspaceMetadataVersionService,
|
||||
private readonly workspacePermissionsCacheService: WorkspacePermissionsCacheService,
|
||||
private readonly logger: LoggerService,
|
||||
) {}
|
||||
|
||||
run = async ({
|
||||
@@ -26,6 +32,9 @@ export class WorkspaceMigrationRunnerV2Service {
|
||||
workspaceId,
|
||||
relatedFlatEntityMapsKeys,
|
||||
}: WorkspaceMigrationV2): Promise<AllFlatEntityMaps> => {
|
||||
this.logger.time('Runner', 'Total execution');
|
||||
this.logger.time('Runner', 'Initial cache retrieval');
|
||||
|
||||
const queryRunner = this.coreDataSource.createQueryRunner();
|
||||
|
||||
let allFlatEntityMaps =
|
||||
@@ -36,6 +45,9 @@ export class WorkspaceMigrationRunnerV2Service {
|
||||
},
|
||||
);
|
||||
|
||||
this.logger.timeEnd('Runner', 'Initial cache retrieval');
|
||||
this.logger.time('Runner', 'Transaction execution');
|
||||
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
|
||||
@@ -60,10 +72,8 @@ export class WorkspaceMigrationRunnerV2Service {
|
||||
) as (keyof AllFlatEntityMaps)[];
|
||||
|
||||
flatEntityMapsToInvalidate = [
|
||||
...new Set([
|
||||
...optimisticallyUpdatedFlatEntityMapsKeys,
|
||||
...flatEntityMapsToInvalidate,
|
||||
]),
|
||||
...optimisticallyUpdatedFlatEntityMapsKeys,
|
||||
...flatEntityMapsToInvalidate,
|
||||
];
|
||||
|
||||
allFlatEntityMaps = {
|
||||
@@ -74,11 +84,44 @@ export class WorkspaceMigrationRunnerV2Service {
|
||||
|
||||
await queryRunner.commitTransaction();
|
||||
|
||||
this.logger.timeEnd('Runner', 'Transaction execution');
|
||||
this.logger.time('Runner', 'Cache invalidation');
|
||||
|
||||
const flatEntitiesCacheToInvalidate = [
|
||||
...new Set([
|
||||
...flatEntityMapsToInvalidate,
|
||||
...(relatedFlatEntityMapsKeys ?? []),
|
||||
]),
|
||||
];
|
||||
|
||||
if (
|
||||
flatEntitiesCacheToInvalidate.includes('flatObjectMetadataMaps') ||
|
||||
flatEntitiesCacheToInvalidate.includes('flatFieldMetadataMaps')
|
||||
) {
|
||||
// Temporarily invalidation old cache too until it's deprecated
|
||||
await this.workspaceMetadataVersionService.incrementMetadataVersion(
|
||||
workspaceId,
|
||||
);
|
||||
await this.workspacePermissionsCacheService.recomputeRolesPermissionsCache(
|
||||
{
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
await this.flatEntityMapsCacheService.invalidateFlatEntityMaps({
|
||||
workspaceId,
|
||||
flatEntities: flatEntityMapsToInvalidate,
|
||||
flatEntities: [
|
||||
...new Set([
|
||||
...flatEntityMapsToInvalidate,
|
||||
...(relatedFlatEntityMapsKeys ?? []),
|
||||
]),
|
||||
],
|
||||
});
|
||||
|
||||
this.logger.timeEnd('Runner', 'Cache invalidation');
|
||||
this.logger.timeEnd('Runner', 'Total execution');
|
||||
|
||||
return allFlatEntityMaps;
|
||||
} catch (error) {
|
||||
if (queryRunner.isTransactionActive) {
|
||||
|
||||
+10
-10
@@ -42,7 +42,7 @@ describe('Generate Column Definitions', () => {
|
||||
|
||||
const columns = generateColumnDefinitions({
|
||||
flatFieldMetadata: enumField,
|
||||
flatObjectMetadataWithoutFields: mockObjectMetadata,
|
||||
flatObjectMetadata: mockObjectMetadata,
|
||||
});
|
||||
|
||||
expect(columns).toHaveLength(1);
|
||||
@@ -85,7 +85,7 @@ describe('Generate Column Definitions', () => {
|
||||
|
||||
const columns = generateColumnDefinitions({
|
||||
flatFieldMetadata: multiSelectField,
|
||||
flatObjectMetadataWithoutFields: mockObjectMetadata,
|
||||
flatObjectMetadata: mockObjectMetadata,
|
||||
});
|
||||
|
||||
expect(columns).toHaveLength(1);
|
||||
@@ -115,7 +115,7 @@ describe('Generate Column Definitions', () => {
|
||||
|
||||
const columns = generateColumnDefinitions({
|
||||
flatFieldMetadata: relationField,
|
||||
flatObjectMetadataWithoutFields: mockObjectMetadata,
|
||||
flatObjectMetadata: mockObjectMetadata,
|
||||
});
|
||||
|
||||
// Relations without join columns must return empty array
|
||||
@@ -136,7 +136,7 @@ describe('Generate Column Definitions', () => {
|
||||
|
||||
const columns = generateColumnDefinitions({
|
||||
flatFieldMetadata: relationField,
|
||||
flatObjectMetadataWithoutFields: mockObjectMetadata,
|
||||
flatObjectMetadata: mockObjectMetadata,
|
||||
});
|
||||
|
||||
expect(columns).toHaveLength(1);
|
||||
@@ -166,7 +166,7 @@ describe('Generate Column Definitions', () => {
|
||||
|
||||
const columns = generateColumnDefinitions({
|
||||
flatFieldMetadata: addressField,
|
||||
flatObjectMetadataWithoutFields: mockObjectMetadata,
|
||||
flatObjectMetadata: mockObjectMetadata,
|
||||
});
|
||||
|
||||
expect(columns).toHaveLength(8);
|
||||
@@ -206,7 +206,7 @@ describe('Generate Column Definitions', () => {
|
||||
|
||||
const columns = generateColumnDefinitions({
|
||||
flatFieldMetadata: currencyField,
|
||||
flatObjectMetadataWithoutFields: mockObjectMetadata,
|
||||
flatObjectMetadata: mockObjectMetadata,
|
||||
});
|
||||
|
||||
expect(columns).toHaveLength(2);
|
||||
@@ -245,7 +245,7 @@ describe('Generate Column Definitions', () => {
|
||||
|
||||
const columns = generateColumnDefinitions({
|
||||
flatFieldMetadata: textField,
|
||||
flatObjectMetadataWithoutFields: mockObjectMetadata,
|
||||
flatObjectMetadata: mockObjectMetadata,
|
||||
});
|
||||
|
||||
expect(columns).toStrictEqual([
|
||||
@@ -271,7 +271,7 @@ describe('Generate Column Definitions', () => {
|
||||
|
||||
const columns = generateColumnDefinitions({
|
||||
flatFieldMetadata: booleanField,
|
||||
flatObjectMetadataWithoutFields: mockObjectMetadata,
|
||||
flatObjectMetadata: mockObjectMetadata,
|
||||
});
|
||||
|
||||
expect(columns).toStrictEqual([
|
||||
@@ -298,7 +298,7 @@ describe('Generate Column Definitions', () => {
|
||||
|
||||
const columns = generateColumnDefinitions({
|
||||
flatFieldMetadata: textField,
|
||||
flatObjectMetadataWithoutFields: mockObjectMetadata,
|
||||
flatObjectMetadata: mockObjectMetadata,
|
||||
});
|
||||
|
||||
expect(columns).toStrictEqual([
|
||||
@@ -323,7 +323,7 @@ describe('Generate Column Definitions', () => {
|
||||
|
||||
const columns = generateColumnDefinitions({
|
||||
flatFieldMetadata: uuidField,
|
||||
flatObjectMetadataWithoutFields: mockObjectMetadata,
|
||||
flatObjectMetadata: mockObjectMetadata,
|
||||
});
|
||||
|
||||
expect(columns).toStrictEqual([
|
||||
|
||||
+22
-16
@@ -13,7 +13,7 @@ import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-m
|
||||
import { isCompositeFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-composite-flat-field-metadata.util';
|
||||
import { isFlatFieldMetadataOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util';
|
||||
import { isMorphOrRelationFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-metadata.util';
|
||||
import { type FlatObjectMetadataWithoutFields } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { fieldMetadataTypeToColumnType } from 'src/engine/metadata-modules/workspace-migration/utils/field-metadata-type-to-column-type.util';
|
||||
import { type WorkspaceSchemaColumnDefinition } from 'src/engine/twenty-orm/workspace-schema-manager/types/workspace-schema-column-definition.type';
|
||||
import { computePostgresEnumName } from 'src/engine/workspace-manager/workspace-migration-runner/utils/compute-postgres-enum-name.util';
|
||||
@@ -27,15 +27,15 @@ import { getWorkspaceSchemaContextForMigration } from 'src/engine/workspace-mana
|
||||
export const generateCompositeColumnDefinition = ({
|
||||
compositeProperty,
|
||||
parentFieldMetadata,
|
||||
flatObjectMetadataWithoutFields,
|
||||
flatObjectMetadata,
|
||||
}: {
|
||||
compositeProperty: CompositeProperty;
|
||||
parentFieldMetadata: FlatFieldMetadata<CompositeFieldMetadataType>;
|
||||
flatObjectMetadataWithoutFields: FlatObjectMetadataWithoutFields;
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
}): WorkspaceSchemaColumnDefinition => {
|
||||
const { tableName, schemaName } = getWorkspaceSchemaContextForMigration({
|
||||
workspaceId: flatObjectMetadataWithoutFields.workspaceId,
|
||||
flatObjectMetadata: flatObjectMetadataWithoutFields,
|
||||
workspaceId: flatObjectMetadata.workspaceId,
|
||||
flatObjectMetadata,
|
||||
});
|
||||
|
||||
if (
|
||||
@@ -125,11 +125,15 @@ const generateRelationColumnDefinition = (
|
||||
};
|
||||
};
|
||||
|
||||
const generateColumnDefinition = (
|
||||
flatFieldMetadata: FlatFieldMetadata,
|
||||
tableName: string,
|
||||
schemaName: string,
|
||||
): WorkspaceSchemaColumnDefinition => {
|
||||
const generateColumnDefinition = ({
|
||||
flatFieldMetadata,
|
||||
schemaName,
|
||||
tableName,
|
||||
}: {
|
||||
flatFieldMetadata: FlatFieldMetadata;
|
||||
tableName: string;
|
||||
schemaName: string;
|
||||
}): WorkspaceSchemaColumnDefinition => {
|
||||
const columnName = computeColumnName(flatFieldMetadata.name);
|
||||
const columnType = fieldMetadataTypeToColumnType(
|
||||
flatFieldMetadata.type,
|
||||
@@ -159,14 +163,14 @@ const generateColumnDefinition = (
|
||||
|
||||
export const generateColumnDefinitions = ({
|
||||
flatFieldMetadata,
|
||||
flatObjectMetadataWithoutFields,
|
||||
flatObjectMetadata,
|
||||
}: {
|
||||
flatFieldMetadata: FlatFieldMetadata;
|
||||
flatObjectMetadataWithoutFields: FlatObjectMetadataWithoutFields;
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
}): WorkspaceSchemaColumnDefinition[] => {
|
||||
const { tableName, schemaName } = getWorkspaceSchemaContextForMigration({
|
||||
workspaceId: flatObjectMetadataWithoutFields.workspaceId,
|
||||
flatObjectMetadata: flatObjectMetadataWithoutFields,
|
||||
workspaceId: flatObjectMetadata.workspaceId,
|
||||
flatObjectMetadata,
|
||||
});
|
||||
|
||||
if (isCompositeFlatFieldMetadata(flatFieldMetadata)) {
|
||||
@@ -176,7 +180,7 @@ export const generateColumnDefinitions = ({
|
||||
generateCompositeColumnDefinition({
|
||||
compositeProperty: property,
|
||||
parentFieldMetadata: flatFieldMetadata,
|
||||
flatObjectMetadataWithoutFields: flatObjectMetadataWithoutFields,
|
||||
flatObjectMetadata,
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -193,5 +197,7 @@ export const generateColumnDefinitions = ({
|
||||
return relationColumn ? [relationColumn] : [];
|
||||
}
|
||||
|
||||
return [generateColumnDefinition(flatFieldMetadata, tableName, schemaName)];
|
||||
return [
|
||||
generateColumnDefinition({ flatFieldMetadata, tableName, schemaName }),
|
||||
];
|
||||
};
|
||||
|
||||
+2
-5
@@ -1,7 +1,4 @@
|
||||
import {
|
||||
type FlatObjectMetadata,
|
||||
type FlatObjectMetadataWithoutFields,
|
||||
} from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
|
||||
import { getWorkspaceSchemaName } from 'src/engine/workspace-datasource/utils/get-workspace-schema-name.util';
|
||||
|
||||
@@ -15,7 +12,7 @@ export const getWorkspaceSchemaContextForMigration = ({
|
||||
flatObjectMetadata,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
flatObjectMetadata: FlatObjectMetadata | FlatObjectMetadataWithoutFields;
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
}): WorkspaceSchemaContextForMigration => {
|
||||
return {
|
||||
schemaName: getWorkspaceSchemaName(workspaceId),
|
||||
|
||||
+4
@@ -6,6 +6,8 @@ import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/core-mod
|
||||
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
||||
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
|
||||
import { WorkspaceMetadataCacheModule } from 'src/engine/metadata-modules/workspace-metadata-cache/workspace-metadata-cache.module';
|
||||
import { WorkspaceMetadataVersionModule } from 'src/engine/metadata-modules/workspace-metadata-version/workspace-metadata-version.module';
|
||||
import { WorkspacePermissionsCacheModule } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.module';
|
||||
import { WorkspaceSchemaMigrationRunnerActionHandlersModule } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/workspace-schema-migration-runner-action-handlers.module';
|
||||
import { WorkspaceMigrationRunnerActionHandlerRegistryService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/registry/workspace-migration-runner-action-handler-registry.service';
|
||||
import { WorkspaceMigrationRunnerV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/services/workspace-migration-runner-v2.service';
|
||||
@@ -15,6 +17,8 @@ import { WorkspaceMigrationRunnerV2Service } from 'src/engine/workspace-manager/
|
||||
FeatureFlagModule,
|
||||
TypeORMModule,
|
||||
DataSourceModule,
|
||||
WorkspaceMetadataVersionModule,
|
||||
WorkspacePermissionsCacheModule,
|
||||
WorkspaceMetadataCacheModule,
|
||||
WorkspaceSchemaMigrationRunnerActionHandlersModule,
|
||||
WorkspaceManyOrAllFlatEntityMapsCacheModule,
|
||||
|
||||
+2
@@ -1,6 +1,7 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/core-modules/common/services/workspace-many-or-all-flat-entity-maps-cache.module';
|
||||
import { FeatureFlag } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
||||
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
@@ -40,6 +41,7 @@ import { WorkspaceSyncMetadataService } from 'src/engine/workspace-manager/works
|
||||
TypeOrmModule.forFeature([Workspace, FeatureFlag]),
|
||||
WorkspaceMetadataVersionModule,
|
||||
AgentRoleModule,
|
||||
WorkspaceManyOrAllFlatEntityMapsCacheModule,
|
||||
],
|
||||
providers: [
|
||||
...workspaceSyncMetadataFactories,
|
||||
|
||||
+6
@@ -5,6 +5,7 @@ import { DataSource, QueryFailedError } from 'typeorm';
|
||||
|
||||
import { type WorkspaceSyncContext } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/workspace-sync-context.interface';
|
||||
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/core-modules/common/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
||||
import { WorkspaceMetadataVersionService } from 'src/engine/metadata-modules/workspace-metadata-version/services/workspace-metadata-version.service';
|
||||
import {
|
||||
WorkspaceMigrationEntity,
|
||||
@@ -40,6 +41,7 @@ export class WorkspaceSyncMetadataService {
|
||||
private readonly workspaceMetadataVersionService: WorkspaceMetadataVersionService,
|
||||
private readonly workspaceSyncRoleService: WorkspaceSyncRoleService,
|
||||
private readonly workspaceSyncAgentService: WorkspaceSyncAgentService,
|
||||
private readonly flatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -260,6 +262,10 @@ export class WorkspaceSyncMetadataService {
|
||||
await this.workspaceMetadataVersionService.incrementMetadataVersion(
|
||||
context.workspaceId,
|
||||
);
|
||||
await this.flatEntityMapsCacheService.invalidateFlatEntityMaps({
|
||||
workspaceId: context.workspaceId,
|
||||
flatEntities: ['flatObjectMetadataMaps', 'flatFieldMetadataMaps'],
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user