[OBJECT_MANIFEST_BREAKING_CHANGE] Sync returns workspace migration (#17918)
# Introduction In this PR we start returning a workspace migration post sync so it can committed and provided within the tarball ## Universal aggregators utils Created two utils ### deleteUniversalFlatEntityForeignKeyAggregators Used when building a universal create action, a newly created actions should not contain any aggregated foreign key so they won't be codegen in the workspace migration but also they are overriden at uninversal to flat transpilation anw ### resetUniversalFlatEntityForeignKeyAggregators Used before validating a new flat entity creation, some validator will consume the fk aggregator in order to validate integrity, but of optimstically provided it can result to errors. To avoid caller responsability we override them here ## create-field-action refactor Refactored the universal and flat field create action to be following the base actions in order to ease typing Also it was tailored to handle unlimited amount of flat field metadata in the same actions whereas in the reality we were always only sending at max 2 ( for relation fields ) Note: relation field has to be provided at the same as if not optimistic would fail to retrieve circular universal identifiers ## ObjectManifest Now always expect a `labelIdentifierFieldMetadataUniversalIdentifier` ## Integration test Created an integration test that creates an app, sync a first manifest and a second implying update workspace migration action generation
This commit is contained in:
+65
-74
@@ -9,10 +9,18 @@ import { type UniversalCreateFieldAction } from 'src/engine/workspace-manager/wo
|
||||
import { type UniversalCreateObjectAction } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/object/types/workspace-migration-object-action';
|
||||
|
||||
describe('aggregateNonRelationFieldsIntoObjectActions', () => {
|
||||
it('should merge non-relation fields into matching create-object actions', () => {
|
||||
it('should merge non-relation field into matching create-object action', () => {
|
||||
const objectUniversalId = 'object-1';
|
||||
const fieldUniversalId = 'field-1';
|
||||
|
||||
const flatFieldMetadata = getFlatFieldMetadataMock({
|
||||
universalIdentifier: fieldUniversalId,
|
||||
objectMetadataId: 'object-metadata-id',
|
||||
objectMetadataUniversalIdentifier: objectUniversalId,
|
||||
type: FieldMetadataType.TEXT,
|
||||
name: 'testField',
|
||||
});
|
||||
|
||||
const input: OrchestratorActionsReport = {
|
||||
...createEmptyOrchestratorActionsReport(),
|
||||
objectMetadata: {
|
||||
@@ -36,18 +44,8 @@ describe('aggregateNonRelationFieldsIntoObjectActions', () => {
|
||||
{
|
||||
type: 'create',
|
||||
metadataName: 'fieldMetadata',
|
||||
universalFlatFieldMetadatas: [
|
||||
getFlatFieldMetadataMock({
|
||||
universalIdentifier: fieldUniversalId,
|
||||
objectMetadataId: 'object-metadata-id',
|
||||
objectMetadataUniversalIdentifier: objectUniversalId,
|
||||
type: FieldMetadataType.TEXT,
|
||||
name: 'testField',
|
||||
}),
|
||||
],
|
||||
fieldIdByUniversalIdentifier: {
|
||||
[fieldUniversalId]: 'generated-field-id',
|
||||
},
|
||||
flatEntity: flatFieldMetadata,
|
||||
id: 'generated-field-id',
|
||||
} satisfies UniversalCreateFieldAction,
|
||||
],
|
||||
update: [],
|
||||
@@ -75,11 +73,21 @@ describe('aggregateNonRelationFieldsIntoObjectActions', () => {
|
||||
expect(result.fieldMetadata.create).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should keep relation fields in separate create-field actions', () => {
|
||||
it('should keep relation field in separate create-field action', () => {
|
||||
const objectUniversalId = 'object-1';
|
||||
const relationFieldUniversalId = 'relation-field-1';
|
||||
const targetFieldUniversalId = 'target-field-1';
|
||||
|
||||
const flatRelationFieldMetadata = getFlatFieldMetadataMock({
|
||||
universalIdentifier: relationFieldUniversalId,
|
||||
objectMetadataId: 'object-metadata-id',
|
||||
objectMetadataUniversalIdentifier: objectUniversalId,
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'relationField',
|
||||
relationTargetFieldMetadataUniversalIdentifier: targetFieldUniversalId,
|
||||
relationTargetObjectMetadataUniversalIdentifier: 'other-object',
|
||||
});
|
||||
|
||||
const input: OrchestratorActionsReport = {
|
||||
...createEmptyOrchestratorActionsReport(),
|
||||
objectMetadata: {
|
||||
@@ -103,21 +111,8 @@ describe('aggregateNonRelationFieldsIntoObjectActions', () => {
|
||||
{
|
||||
type: 'create',
|
||||
metadataName: 'fieldMetadata',
|
||||
universalFlatFieldMetadatas: [
|
||||
getFlatFieldMetadataMock({
|
||||
universalIdentifier: relationFieldUniversalId,
|
||||
objectMetadataId: 'object-metadata-id',
|
||||
objectMetadataUniversalIdentifier: objectUniversalId,
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'relationField',
|
||||
relationTargetFieldMetadataUniversalIdentifier:
|
||||
targetFieldUniversalId,
|
||||
relationTargetObjectMetadataUniversalIdentifier: 'other-object',
|
||||
}),
|
||||
],
|
||||
fieldIdByUniversalIdentifier: {
|
||||
[relationFieldUniversalId]: 'generated-relation-field-id',
|
||||
},
|
||||
flatEntity: flatRelationFieldMetadata,
|
||||
id: 'generated-relation-field-id',
|
||||
} satisfies UniversalCreateFieldAction,
|
||||
],
|
||||
update: [],
|
||||
@@ -137,17 +132,23 @@ describe('aggregateNonRelationFieldsIntoObjectActions', () => {
|
||||
// Relation field should remain in field actions
|
||||
expect(result.fieldMetadata.create).toMatchObject([
|
||||
{
|
||||
universalFlatFieldMetadatas: [
|
||||
{ universalIdentifier: relationFieldUniversalId },
|
||||
],
|
||||
flatEntity: { universalIdentifier: relationFieldUniversalId },
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should keep fields for existing objects in separate create-field actions', () => {
|
||||
it('should keep field for existing object in separate create-field action', () => {
|
||||
const existingObjectUniversalId = 'existing-object';
|
||||
const fieldUniversalId = 'field-for-existing-object';
|
||||
|
||||
const flatFieldMetadata = getFlatFieldMetadataMock({
|
||||
universalIdentifier: fieldUniversalId,
|
||||
objectMetadataId: 'existing-object-metadata-id',
|
||||
objectMetadataUniversalIdentifier: existingObjectUniversalId,
|
||||
type: FieldMetadataType.TEXT,
|
||||
name: 'fieldForExistingObject',
|
||||
});
|
||||
|
||||
const input: OrchestratorActionsReport = {
|
||||
...createEmptyOrchestratorActionsReport(),
|
||||
objectMetadata: {
|
||||
@@ -160,18 +161,8 @@ describe('aggregateNonRelationFieldsIntoObjectActions', () => {
|
||||
{
|
||||
type: 'create',
|
||||
metadataName: 'fieldMetadata',
|
||||
universalFlatFieldMetadatas: [
|
||||
getFlatFieldMetadataMock({
|
||||
universalIdentifier: fieldUniversalId,
|
||||
objectMetadataId: 'existing-object-metadata-id',
|
||||
objectMetadataUniversalIdentifier: existingObjectUniversalId,
|
||||
type: FieldMetadataType.TEXT,
|
||||
name: 'fieldForExistingObject',
|
||||
}),
|
||||
],
|
||||
fieldIdByUniversalIdentifier: {
|
||||
[fieldUniversalId]: 'generated-field-id',
|
||||
},
|
||||
flatEntity: flatFieldMetadata,
|
||||
id: 'generated-field-id',
|
||||
} satisfies UniversalCreateFieldAction,
|
||||
],
|
||||
update: [],
|
||||
@@ -189,18 +180,34 @@ describe('aggregateNonRelationFieldsIntoObjectActions', () => {
|
||||
// Field should remain in field actions (no matching object to merge into)
|
||||
expect(result.fieldMetadata.create).toMatchObject([
|
||||
{
|
||||
universalFlatFieldMetadatas: [
|
||||
{ universalIdentifier: fieldUniversalId },
|
||||
],
|
||||
flatEntity: { universalIdentifier: fieldUniversalId },
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle mixed relation and non-relation fields', () => {
|
||||
it('should handle mixed relation and non-relation fields as separate actions', () => {
|
||||
const objectUniversalId = 'object-1';
|
||||
const textFieldUniversalId = 'text-field';
|
||||
const relationFieldUniversalId = 'relation-field';
|
||||
|
||||
const flatTextFieldMetadata = getFlatFieldMetadataMock({
|
||||
universalIdentifier: textFieldUniversalId,
|
||||
objectMetadataId: 'object-metadata-id',
|
||||
objectMetadataUniversalIdentifier: objectUniversalId,
|
||||
type: FieldMetadataType.TEXT,
|
||||
name: 'textField',
|
||||
});
|
||||
|
||||
const flatRelationFieldMetadata = getFlatFieldMetadataMock({
|
||||
universalIdentifier: relationFieldUniversalId,
|
||||
objectMetadataId: 'object-metadata-id',
|
||||
objectMetadataUniversalIdentifier: objectUniversalId,
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'relationField',
|
||||
relationTargetFieldMetadataUniversalIdentifier: 'target-field',
|
||||
relationTargetObjectMetadataUniversalIdentifier: 'other-object',
|
||||
});
|
||||
|
||||
const input: OrchestratorActionsReport = {
|
||||
...createEmptyOrchestratorActionsReport(),
|
||||
objectMetadata: {
|
||||
@@ -224,28 +231,14 @@ describe('aggregateNonRelationFieldsIntoObjectActions', () => {
|
||||
{
|
||||
type: 'create',
|
||||
metadataName: 'fieldMetadata',
|
||||
universalFlatFieldMetadatas: [
|
||||
getFlatFieldMetadataMock({
|
||||
universalIdentifier: textFieldUniversalId,
|
||||
objectMetadataId: 'object-metadata-id',
|
||||
objectMetadataUniversalIdentifier: objectUniversalId,
|
||||
type: FieldMetadataType.TEXT,
|
||||
name: 'textField',
|
||||
}),
|
||||
getFlatFieldMetadataMock({
|
||||
universalIdentifier: relationFieldUniversalId,
|
||||
objectMetadataId: 'object-metadata-id',
|
||||
objectMetadataUniversalIdentifier: objectUniversalId,
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'relationField',
|
||||
relationTargetFieldMetadataUniversalIdentifier: 'target-field',
|
||||
relationTargetObjectMetadataUniversalIdentifier: 'other-object',
|
||||
}),
|
||||
],
|
||||
fieldIdByUniversalIdentifier: {
|
||||
[textFieldUniversalId]: 'text-field-id',
|
||||
[relationFieldUniversalId]: 'relation-field-id',
|
||||
},
|
||||
flatEntity: flatTextFieldMetadata,
|
||||
id: 'text-field-id',
|
||||
} satisfies UniversalCreateFieldAction,
|
||||
{
|
||||
type: 'create',
|
||||
metadataName: 'fieldMetadata',
|
||||
flatEntity: flatRelationFieldMetadata,
|
||||
id: 'relation-field-id',
|
||||
} satisfies UniversalCreateFieldAction,
|
||||
],
|
||||
update: [],
|
||||
@@ -269,9 +262,7 @@ describe('aggregateNonRelationFieldsIntoObjectActions', () => {
|
||||
// Relation field should remain in field actions
|
||||
expect(result.fieldMetadata.create).toMatchObject([
|
||||
{
|
||||
universalFlatFieldMetadatas: [
|
||||
{ universalIdentifier: relationFieldUniversalId },
|
||||
],
|
||||
flatEntity: { universalIdentifier: relationFieldUniversalId },
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
+111
-136
@@ -7,7 +7,7 @@ import { aggregateRelationFieldPairs } from 'src/engine/workspace-manager/worksp
|
||||
import { type UniversalCreateFieldAction } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/field/types/workspace-migration-field-action';
|
||||
|
||||
describe('aggregateRelationFieldPairs', () => {
|
||||
it('should bundle relation field pairs into single action with merged fieldIdByUniversalIdentifier', () => {
|
||||
it('should bundle relation field pairs into single action with relatedFieldId', () => {
|
||||
const attachmentObjectId = 'attachment-object';
|
||||
const taskObjectId = 'task-object';
|
||||
const targetTaskFieldId = 'target-task-field';
|
||||
@@ -20,41 +20,32 @@ describe('aggregateRelationFieldPairs', () => {
|
||||
{
|
||||
type: 'create',
|
||||
metadataName: 'fieldMetadata',
|
||||
universalFlatFieldMetadatas: [
|
||||
getFlatFieldMetadataMock({
|
||||
universalIdentifier: targetTaskFieldId,
|
||||
objectMetadataId: 'attachment-object-metadata-id',
|
||||
objectMetadataUniversalIdentifier: attachmentObjectId,
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'targetTask',
|
||||
relationTargetFieldMetadataUniversalIdentifier:
|
||||
attachmentsFieldId,
|
||||
relationTargetObjectMetadataUniversalIdentifier: taskObjectId,
|
||||
}),
|
||||
],
|
||||
fieldIdByUniversalIdentifier: {
|
||||
[targetTaskFieldId]: 'target-task-generated-id',
|
||||
},
|
||||
flatEntity: getFlatFieldMetadataMock({
|
||||
universalIdentifier: targetTaskFieldId,
|
||||
objectMetadataId: 'attachment-object-metadata-id',
|
||||
objectMetadataUniversalIdentifier: attachmentObjectId,
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'targetTask',
|
||||
relationTargetFieldMetadataUniversalIdentifier:
|
||||
attachmentsFieldId,
|
||||
relationTargetObjectMetadataUniversalIdentifier: taskObjectId,
|
||||
}),
|
||||
id: 'target-task-generated-id',
|
||||
} satisfies UniversalCreateFieldAction,
|
||||
{
|
||||
type: 'create',
|
||||
metadataName: 'fieldMetadata',
|
||||
universalFlatFieldMetadatas: [
|
||||
getFlatFieldMetadataMock({
|
||||
universalIdentifier: attachmentsFieldId,
|
||||
objectMetadataId: 'task-object-metadata-id',
|
||||
objectMetadataUniversalIdentifier: taskObjectId,
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'attachments',
|
||||
relationTargetFieldMetadataUniversalIdentifier:
|
||||
targetTaskFieldId,
|
||||
relationTargetObjectMetadataUniversalIdentifier:
|
||||
attachmentObjectId,
|
||||
}),
|
||||
],
|
||||
fieldIdByUniversalIdentifier: {
|
||||
[attachmentsFieldId]: 'attachments-generated-id',
|
||||
},
|
||||
flatEntity: getFlatFieldMetadataMock({
|
||||
universalIdentifier: attachmentsFieldId,
|
||||
objectMetadataId: 'task-object-metadata-id',
|
||||
objectMetadataUniversalIdentifier: taskObjectId,
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'attachments',
|
||||
relationTargetFieldMetadataUniversalIdentifier: targetTaskFieldId,
|
||||
relationTargetObjectMetadataUniversalIdentifier:
|
||||
attachmentObjectId,
|
||||
}),
|
||||
id: 'attachments-generated-id',
|
||||
} satisfies UniversalCreateFieldAction,
|
||||
],
|
||||
update: [],
|
||||
@@ -66,17 +57,15 @@ describe('aggregateRelationFieldPairs', () => {
|
||||
orchestratorActionsReport: input,
|
||||
});
|
||||
|
||||
// Should be bundled into a single action with both fields and merged ID map
|
||||
// Should be bundled into a single action with flatEntity and relatedUniversalFlatFieldMetadata
|
||||
expect(result.fieldMetadata.create).toMatchObject([
|
||||
{
|
||||
universalFlatFieldMetadatas: [
|
||||
{ universalIdentifier: targetTaskFieldId },
|
||||
{ universalIdentifier: attachmentsFieldId },
|
||||
],
|
||||
fieldIdByUniversalIdentifier: {
|
||||
[targetTaskFieldId]: 'target-task-generated-id',
|
||||
[attachmentsFieldId]: 'attachments-generated-id',
|
||||
flatEntity: { universalIdentifier: targetTaskFieldId },
|
||||
id: 'target-task-generated-id',
|
||||
relatedUniversalFlatFieldMetadata: {
|
||||
universalIdentifier: attachmentsFieldId,
|
||||
},
|
||||
relatedFieldId: 'attachments-generated-id',
|
||||
},
|
||||
]);
|
||||
});
|
||||
@@ -92,18 +81,14 @@ describe('aggregateRelationFieldPairs', () => {
|
||||
{
|
||||
type: 'create',
|
||||
metadataName: 'fieldMetadata',
|
||||
universalFlatFieldMetadatas: [
|
||||
getFlatFieldMetadataMock({
|
||||
universalIdentifier: fieldUniversalId,
|
||||
objectMetadataId: 'some-object-metadata-id',
|
||||
objectMetadataUniversalIdentifier: objectUniversalId,
|
||||
type: FieldMetadataType.TEXT,
|
||||
name: 'standaloneField',
|
||||
}),
|
||||
],
|
||||
fieldIdByUniversalIdentifier: {
|
||||
[fieldUniversalId]: 'standalone-field-id',
|
||||
},
|
||||
flatEntity: getFlatFieldMetadataMock({
|
||||
universalIdentifier: fieldUniversalId,
|
||||
objectMetadataId: 'some-object-metadata-id',
|
||||
objectMetadataUniversalIdentifier: objectUniversalId,
|
||||
type: FieldMetadataType.TEXT,
|
||||
name: 'standaloneField',
|
||||
}),
|
||||
id: 'standalone-field-id',
|
||||
} satisfies UniversalCreateFieldAction,
|
||||
],
|
||||
update: [],
|
||||
@@ -115,14 +100,16 @@ describe('aggregateRelationFieldPairs', () => {
|
||||
orchestratorActionsReport: input,
|
||||
});
|
||||
|
||||
// Should remain as single action
|
||||
// Should remain as single action without relatedUniversalFlatFieldMetadata
|
||||
expect(result.fieldMetadata.create).toMatchObject([
|
||||
{
|
||||
universalFlatFieldMetadatas: [
|
||||
{ universalIdentifier: fieldUniversalId },
|
||||
],
|
||||
flatEntity: { universalIdentifier: fieldUniversalId },
|
||||
},
|
||||
]);
|
||||
expect(
|
||||
result.fieldMetadata.create[0].relatedUniversalFlatFieldMetadata,
|
||||
).toBeUndefined();
|
||||
expect(result.fieldMetadata.create[0].relatedFieldId).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should handle relation field with target not being created (existing field)', () => {
|
||||
@@ -137,21 +124,17 @@ describe('aggregateRelationFieldPairs', () => {
|
||||
{
|
||||
type: 'create',
|
||||
metadataName: 'fieldMetadata',
|
||||
universalFlatFieldMetadatas: [
|
||||
getFlatFieldMetadataMock({
|
||||
universalIdentifier: relationFieldId,
|
||||
objectMetadataId: 'some-object-metadata-id',
|
||||
objectMetadataUniversalIdentifier: objectUniversalId,
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'relationToExisting',
|
||||
relationTargetFieldMetadataUniversalIdentifier:
|
||||
existingTargetFieldId,
|
||||
relationTargetObjectMetadataUniversalIdentifier: 'other-object',
|
||||
}),
|
||||
],
|
||||
fieldIdByUniversalIdentifier: {
|
||||
[relationFieldId]: 'new-relation-field-id',
|
||||
},
|
||||
flatEntity: getFlatFieldMetadataMock({
|
||||
universalIdentifier: relationFieldId,
|
||||
objectMetadataId: 'some-object-metadata-id',
|
||||
objectMetadataUniversalIdentifier: objectUniversalId,
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'relationToExisting',
|
||||
relationTargetFieldMetadataUniversalIdentifier:
|
||||
existingTargetFieldId,
|
||||
relationTargetObjectMetadataUniversalIdentifier: 'other-object',
|
||||
}),
|
||||
id: 'new-relation-field-id',
|
||||
} satisfies UniversalCreateFieldAction,
|
||||
],
|
||||
update: [],
|
||||
@@ -163,12 +146,16 @@ describe('aggregateRelationFieldPairs', () => {
|
||||
orchestratorActionsReport: input,
|
||||
});
|
||||
|
||||
// Should remain as single action (target not being created)
|
||||
// Should remain as single action without relatedUniversalFlatFieldMetadata (target not being created)
|
||||
expect(result.fieldMetadata.create).toMatchObject([
|
||||
{
|
||||
universalFlatFieldMetadatas: [{ universalIdentifier: relationFieldId }],
|
||||
flatEntity: { universalIdentifier: relationFieldId },
|
||||
},
|
||||
]);
|
||||
expect(
|
||||
result.fieldMetadata.create[0].relatedUniversalFlatFieldMetadata,
|
||||
).toBeUndefined();
|
||||
expect(result.fieldMetadata.create[0].relatedFieldId).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should handle multiple independent relation pairs', () => {
|
||||
@@ -184,66 +171,58 @@ describe('aggregateRelationFieldPairs', () => {
|
||||
{
|
||||
type: 'create',
|
||||
metadataName: 'fieldMetadata',
|
||||
universalFlatFieldMetadatas: [
|
||||
getFlatFieldMetadataMock({
|
||||
universalIdentifier: pair1FieldA,
|
||||
objectMetadataId: 'object-1-metadata-id',
|
||||
objectMetadataUniversalIdentifier: 'object-1',
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'pair1FieldA',
|
||||
relationTargetFieldMetadataUniversalIdentifier: pair1FieldB,
|
||||
relationTargetObjectMetadataUniversalIdentifier: 'object-2',
|
||||
}),
|
||||
],
|
||||
fieldIdByUniversalIdentifier: { [pair1FieldA]: 'id-1a' },
|
||||
flatEntity: getFlatFieldMetadataMock({
|
||||
universalIdentifier: pair1FieldA,
|
||||
objectMetadataId: 'object-1-metadata-id',
|
||||
objectMetadataUniversalIdentifier: 'object-1',
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'pair1FieldA',
|
||||
relationTargetFieldMetadataUniversalIdentifier: pair1FieldB,
|
||||
relationTargetObjectMetadataUniversalIdentifier: 'object-2',
|
||||
}),
|
||||
id: 'id-1a',
|
||||
} satisfies UniversalCreateFieldAction,
|
||||
{
|
||||
type: 'create',
|
||||
metadataName: 'fieldMetadata',
|
||||
universalFlatFieldMetadatas: [
|
||||
getFlatFieldMetadataMock({
|
||||
universalIdentifier: pair1FieldB,
|
||||
objectMetadataId: 'object-2-metadata-id',
|
||||
objectMetadataUniversalIdentifier: 'object-2',
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'pair1FieldB',
|
||||
relationTargetFieldMetadataUniversalIdentifier: pair1FieldA,
|
||||
relationTargetObjectMetadataUniversalIdentifier: 'object-1',
|
||||
}),
|
||||
],
|
||||
fieldIdByUniversalIdentifier: { [pair1FieldB]: 'id-1b' },
|
||||
flatEntity: getFlatFieldMetadataMock({
|
||||
universalIdentifier: pair1FieldB,
|
||||
objectMetadataId: 'object-2-metadata-id',
|
||||
objectMetadataUniversalIdentifier: 'object-2',
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'pair1FieldB',
|
||||
relationTargetFieldMetadataUniversalIdentifier: pair1FieldA,
|
||||
relationTargetObjectMetadataUniversalIdentifier: 'object-1',
|
||||
}),
|
||||
id: 'id-1b',
|
||||
} satisfies UniversalCreateFieldAction,
|
||||
{
|
||||
type: 'create',
|
||||
metadataName: 'fieldMetadata',
|
||||
universalFlatFieldMetadatas: [
|
||||
getFlatFieldMetadataMock({
|
||||
universalIdentifier: pair2FieldA,
|
||||
objectMetadataId: 'object-3-metadata-id',
|
||||
objectMetadataUniversalIdentifier: 'object-3',
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'pair2FieldA',
|
||||
relationTargetFieldMetadataUniversalIdentifier: pair2FieldB,
|
||||
relationTargetObjectMetadataUniversalIdentifier: 'object-4',
|
||||
}),
|
||||
],
|
||||
fieldIdByUniversalIdentifier: { [pair2FieldA]: 'id-2a' },
|
||||
flatEntity: getFlatFieldMetadataMock({
|
||||
universalIdentifier: pair2FieldA,
|
||||
objectMetadataId: 'object-3-metadata-id',
|
||||
objectMetadataUniversalIdentifier: 'object-3',
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'pair2FieldA',
|
||||
relationTargetFieldMetadataUniversalIdentifier: pair2FieldB,
|
||||
relationTargetObjectMetadataUniversalIdentifier: 'object-4',
|
||||
}),
|
||||
id: 'id-2a',
|
||||
} satisfies UniversalCreateFieldAction,
|
||||
{
|
||||
type: 'create',
|
||||
metadataName: 'fieldMetadata',
|
||||
universalFlatFieldMetadatas: [
|
||||
getFlatFieldMetadataMock({
|
||||
universalIdentifier: pair2FieldB,
|
||||
objectMetadataId: 'object-4-metadata-id',
|
||||
objectMetadataUniversalIdentifier: 'object-4',
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'pair2FieldB',
|
||||
relationTargetFieldMetadataUniversalIdentifier: pair2FieldA,
|
||||
relationTargetObjectMetadataUniversalIdentifier: 'object-3',
|
||||
}),
|
||||
],
|
||||
fieldIdByUniversalIdentifier: { [pair2FieldB]: 'id-2b' },
|
||||
flatEntity: getFlatFieldMetadataMock({
|
||||
universalIdentifier: pair2FieldB,
|
||||
objectMetadataId: 'object-4-metadata-id',
|
||||
objectMetadataUniversalIdentifier: 'object-4',
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'pair2FieldB',
|
||||
relationTargetFieldMetadataUniversalIdentifier: pair2FieldA,
|
||||
relationTargetObjectMetadataUniversalIdentifier: 'object-3',
|
||||
}),
|
||||
id: 'id-2b',
|
||||
} satisfies UniversalCreateFieldAction,
|
||||
],
|
||||
update: [],
|
||||
@@ -255,27 +234,23 @@ describe('aggregateRelationFieldPairs', () => {
|
||||
orchestratorActionsReport: input,
|
||||
});
|
||||
|
||||
// Should result in 2 bundled actions (one per pair), each with merged fieldIdByUniversalIdentifier
|
||||
// Should result in 2 bundled actions (one per pair)
|
||||
expect(result.fieldMetadata.create).toMatchObject([
|
||||
{
|
||||
universalFlatFieldMetadatas: [
|
||||
{ universalIdentifier: pair1FieldA },
|
||||
{ universalIdentifier: pair1FieldB },
|
||||
],
|
||||
fieldIdByUniversalIdentifier: {
|
||||
[pair1FieldA]: 'id-1a',
|
||||
[pair1FieldB]: 'id-1b',
|
||||
flatEntity: { universalIdentifier: pair1FieldA },
|
||||
id: 'id-1a',
|
||||
relatedUniversalFlatFieldMetadata: {
|
||||
universalIdentifier: pair1FieldB,
|
||||
},
|
||||
relatedFieldId: 'id-1b',
|
||||
},
|
||||
{
|
||||
universalFlatFieldMetadatas: [
|
||||
{ universalIdentifier: pair2FieldA },
|
||||
{ universalIdentifier: pair2FieldB },
|
||||
],
|
||||
fieldIdByUniversalIdentifier: {
|
||||
[pair2FieldA]: 'id-2a',
|
||||
[pair2FieldB]: 'id-2b',
|
||||
flatEntity: { universalIdentifier: pair2FieldA },
|
||||
id: 'id-2a',
|
||||
relatedUniversalFlatFieldMetadata: {
|
||||
universalIdentifier: pair2FieldB,
|
||||
},
|
||||
relatedFieldId: 'id-2b',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
+18
-55
@@ -1,10 +1,8 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { isMorphOrRelationUniversalFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-metadata.util';
|
||||
import { isMorphOrRelationFieldMetadataType } from 'src/engine/utils/is-morph-or-relation-field-metadata-type.util';
|
||||
import { type AggregateOrchestratorActionsReportArgs } from 'src/engine/workspace-manager/workspace-migration/types/workspace-migration-aggregate-orchestrator-actions-report-args.type';
|
||||
import { type OrchestratorActionsReport } from 'src/engine/workspace-manager/workspace-migration/types/workspace-migration-orchestrator.type';
|
||||
import { type UniversalFlatFieldMetadata } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-field-metadata.type';
|
||||
import { mergeFieldIdByUniversalIdentifier } from 'src/engine/workspace-manager/workspace-migration/utils/merge-field-id-by-universal-identifier.util';
|
||||
import { type UniversalCreateFieldAction } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/field/types/workspace-migration-field-action';
|
||||
import { type UniversalCreateObjectAction } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/object/types/workspace-migration-object-action';
|
||||
|
||||
@@ -29,64 +27,29 @@ export const aggregateNonRelationFieldsIntoObjectActions = ({
|
||||
const remainingCreateFieldActions: UniversalCreateFieldAction[] = [];
|
||||
|
||||
for (const createFieldAction of createFieldActions) {
|
||||
const nonRelationFields: UniversalFlatFieldMetadata[] = [];
|
||||
const relationFields: UniversalFlatFieldMetadata[] = [];
|
||||
const flatFieldMetadata = createFieldAction.flatEntity;
|
||||
|
||||
for (const field of createFieldAction.universalFlatFieldMetadatas) {
|
||||
if (isMorphOrRelationUniversalFlatFieldMetadata(field)) {
|
||||
relationFields.push(field);
|
||||
} else {
|
||||
nonRelationFields.push(field);
|
||||
}
|
||||
if (isMorphOrRelationFieldMetadataType(flatFieldMetadata.type)) {
|
||||
remainingCreateFieldActions.push(createFieldAction);
|
||||
continue;
|
||||
}
|
||||
|
||||
const nonRelationFieldsByObjectUniversalIdentifier = new Map<
|
||||
string,
|
||||
UniversalFlatFieldMetadata[]
|
||||
>();
|
||||
const objectUniversalId =
|
||||
flatFieldMetadata.objectMetadataUniversalIdentifier;
|
||||
const matchingObjectAction =
|
||||
createObjectActionByObjectUniversalIdentifier.get(objectUniversalId);
|
||||
|
||||
for (const field of nonRelationFields) {
|
||||
const objectUniversalId = field.objectMetadataUniversalIdentifier;
|
||||
const existing =
|
||||
nonRelationFieldsByObjectUniversalIdentifier.get(objectUniversalId);
|
||||
if (isDefined(matchingObjectAction)) {
|
||||
matchingObjectAction.universalFlatFieldMetadatas.push(flatFieldMetadata);
|
||||
|
||||
if (isDefined(existing)) {
|
||||
existing.push(field);
|
||||
} else {
|
||||
nonRelationFieldsByObjectUniversalIdentifier.set(objectUniversalId, [
|
||||
field,
|
||||
]);
|
||||
if (isDefined(createFieldAction.id)) {
|
||||
matchingObjectAction.fieldIdByUniversalIdentifier = {
|
||||
...matchingObjectAction.fieldIdByUniversalIdentifier,
|
||||
[flatFieldMetadata.universalIdentifier]: createFieldAction.id,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const unmergedNonRelationFields: UniversalFlatFieldMetadata[] = [];
|
||||
|
||||
for (const [
|
||||
objectUniversalId,
|
||||
fields,
|
||||
] of nonRelationFieldsByObjectUniversalIdentifier) {
|
||||
const matchingObjectAction =
|
||||
createObjectActionByObjectUniversalIdentifier.get(objectUniversalId);
|
||||
|
||||
if (isDefined(matchingObjectAction)) {
|
||||
matchingObjectAction.universalFlatFieldMetadatas.push(...fields);
|
||||
matchingObjectAction.fieldIdByUniversalIdentifier =
|
||||
mergeFieldIdByUniversalIdentifier(
|
||||
matchingObjectAction.fieldIdByUniversalIdentifier,
|
||||
createFieldAction.fieldIdByUniversalIdentifier,
|
||||
);
|
||||
} else {
|
||||
unmergedNonRelationFields.push(...fields);
|
||||
}
|
||||
}
|
||||
|
||||
const remainingFields = [...unmergedNonRelationFields, ...relationFields];
|
||||
|
||||
if (remainingFields.length > 0) {
|
||||
remainingCreateFieldActions.push({
|
||||
...createFieldAction,
|
||||
universalFlatFieldMetadatas: remainingFields,
|
||||
});
|
||||
} else {
|
||||
remainingCreateFieldActions.push(createFieldAction);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+32
-40
@@ -2,29 +2,23 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type AggregateOrchestratorActionsReportArgs } from 'src/engine/workspace-manager/workspace-migration/types/workspace-migration-aggregate-orchestrator-actions-report-args.type';
|
||||
import { type OrchestratorActionsReport } from 'src/engine/workspace-manager/workspace-migration/types/workspace-migration-orchestrator.type';
|
||||
import { type UniversalFlatFieldMetadata } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-field-metadata.type';
|
||||
import { mergeFieldIdByUniversalIdentifier } from 'src/engine/workspace-manager/workspace-migration/utils/merge-field-id-by-universal-identifier.util';
|
||||
import { type UniversalCreateFieldAction } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/field/types/workspace-migration-field-action';
|
||||
|
||||
type FieldWithActionContext = {
|
||||
field: UniversalFlatFieldMetadata;
|
||||
fieldIdByUniversalIdentifier: Record<string, string> | undefined;
|
||||
};
|
||||
|
||||
export const aggregateRelationFieldPairs = ({
|
||||
orchestratorActionsReport,
|
||||
}: AggregateOrchestratorActionsReportArgs): OrchestratorActionsReport => {
|
||||
const createFieldActions = orchestratorActionsReport.fieldMetadata.create;
|
||||
|
||||
const fieldByUniversalIdentifier = new Map<string, FieldWithActionContext>();
|
||||
const actionByFieldUniversalIdentifier = new Map<
|
||||
string,
|
||||
UniversalCreateFieldAction
|
||||
>();
|
||||
|
||||
for (const action of createFieldActions) {
|
||||
for (const field of action.universalFlatFieldMetadatas) {
|
||||
fieldByUniversalIdentifier.set(field.universalIdentifier, {
|
||||
field,
|
||||
fieldIdByUniversalIdentifier: action.fieldIdByUniversalIdentifier,
|
||||
});
|
||||
}
|
||||
actionByFieldUniversalIdentifier.set(
|
||||
action.flatEntity.universalIdentifier,
|
||||
action,
|
||||
);
|
||||
}
|
||||
|
||||
const processedFieldUniversalIdentifiers = new Set<string>();
|
||||
@@ -32,45 +26,43 @@ export const aggregateRelationFieldPairs = ({
|
||||
|
||||
for (const [
|
||||
universalIdentifier,
|
||||
fieldContext,
|
||||
] of fieldByUniversalIdentifier) {
|
||||
action,
|
||||
] of actionByFieldUniversalIdentifier) {
|
||||
if (processedFieldUniversalIdentifiers.has(universalIdentifier)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const { field, fieldIdByUniversalIdentifier } = fieldContext;
|
||||
|
||||
processedFieldUniversalIdentifiers.add(universalIdentifier);
|
||||
|
||||
const fieldsToBundle: UniversalFlatFieldMetadata[] = [field];
|
||||
let mergedFieldIdMap = fieldIdByUniversalIdentifier;
|
||||
|
||||
const targetUniversalIdentifier =
|
||||
field.relationTargetFieldMetadataUniversalIdentifier;
|
||||
action.flatEntity.relationTargetFieldMetadataUniversalIdentifier;
|
||||
|
||||
if (isDefined(targetUniversalIdentifier)) {
|
||||
const targetFieldContext = fieldByUniversalIdentifier.get(
|
||||
targetUniversalIdentifier,
|
||||
);
|
||||
|
||||
if (
|
||||
isDefined(targetFieldContext) &&
|
||||
!processedFieldUniversalIdentifiers.has(targetUniversalIdentifier)
|
||||
) {
|
||||
fieldsToBundle.push(targetFieldContext.field);
|
||||
processedFieldUniversalIdentifiers.add(targetUniversalIdentifier);
|
||||
mergedFieldIdMap = mergeFieldIdByUniversalIdentifier(
|
||||
mergedFieldIdMap,
|
||||
targetFieldContext.fieldIdByUniversalIdentifier,
|
||||
);
|
||||
}
|
||||
if (!isDefined(targetUniversalIdentifier)) {
|
||||
aggregatedCreateFieldActions.push(action);
|
||||
continue;
|
||||
}
|
||||
|
||||
const targetAction = actionByFieldUniversalIdentifier.get(
|
||||
targetUniversalIdentifier,
|
||||
);
|
||||
|
||||
if (
|
||||
!isDefined(targetAction) ||
|
||||
processedFieldUniversalIdentifiers.has(targetUniversalIdentifier)
|
||||
) {
|
||||
aggregatedCreateFieldActions.push(action);
|
||||
continue;
|
||||
}
|
||||
|
||||
processedFieldUniversalIdentifiers.add(targetUniversalIdentifier);
|
||||
|
||||
aggregatedCreateFieldActions.push({
|
||||
type: 'create',
|
||||
metadataName: 'fieldMetadata',
|
||||
universalFlatFieldMetadatas: fieldsToBundle,
|
||||
fieldIdByUniversalIdentifier: mergedFieldIdMap,
|
||||
flatEntity: action.flatEntity,
|
||||
id: action.id,
|
||||
relatedUniversalFlatFieldMetadata: targetAction.flatEntity,
|
||||
relatedFieldId: targetAction.id,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const mergeFieldIdByUniversalIdentifier = (
|
||||
existing: Record<string, string> | undefined,
|
||||
incoming: Record<string, string> | undefined,
|
||||
): Record<string, string> | undefined => {
|
||||
if (!isDefined(existing) && !isDefined(incoming)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
...existing,
|
||||
...incoming,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user