Refactor builder to embed Object validation create/delete/update (#13934)
# Introduction Moving validation directly in the builder that has the perfect granularity to do it. When importing we won't have to infer and dispatch on the operation nature ( update delete create ) and validate accordingly ## Objects Only migrated object validation for the moment even though create object involves a validate flat field metadata creation call too ## TODO - improve `otherFlatObjectMetadataMapsToValidate` naming too vague ## Next - handle fields validation within fields actions build - Unit test coverage validation issue on builder and validate - integration test plugging with new feature flag ## Manual tested - Update - Delete - Create TODO
This commit is contained in:
+2
-4
@@ -34,9 +34,8 @@ import { WorkspaceMetadataVersionModule } from 'src/engine/metadata-modules/work
|
||||
import { WorkspaceMigrationModule } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.module';
|
||||
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
|
||||
import { WorkspaceMigrationRunnerModule } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.module';
|
||||
import { WorkspaceMigrationBuilderV2Module } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-builder-v2.module';
|
||||
import { WorkspaceMigrationRunnerV2Module } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/workspace-migration-runner-v2.module';
|
||||
import { ViewModule } from 'src/modules/view/view.module';
|
||||
import { WorkspaceMigrationV2Module } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-v2.module';
|
||||
|
||||
import { FieldMetadataEntity } from './field-metadata.entity';
|
||||
|
||||
@@ -64,8 +63,7 @@ import { FieldMetadataService } from './services/field-metadata.service';
|
||||
ViewModule,
|
||||
PermissionsModule,
|
||||
WorkspaceMetadataCacheModule,
|
||||
WorkspaceMigrationBuilderV2Module,
|
||||
WorkspaceMigrationRunnerV2Module,
|
||||
WorkspaceMigrationV2Module,
|
||||
FlatFieldMetadataModule,
|
||||
IndexMetadataModule,
|
||||
],
|
||||
|
||||
+91
-100
@@ -35,8 +35,7 @@ import { getSubFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules
|
||||
import { getSubFlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/get-sub-flat-object-metadata-maps.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 { WorkspaceMetadataCacheService } from 'src/engine/metadata-modules/workspace-metadata-cache/services/workspace-metadata-cache.service';
|
||||
import { WorkspaceMigrationBuilderV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-builder-v2.service';
|
||||
import { WorkspaceMigrationRunnerV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/workspace-migration-runner-v2.service';
|
||||
import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-validate-build-and-run-service';
|
||||
|
||||
@Injectable()
|
||||
export class FieldMetadataServiceV2 {
|
||||
@@ -44,9 +43,8 @@ export class FieldMetadataServiceV2 {
|
||||
@InjectRepository(FieldMetadataEntity, 'core')
|
||||
private readonly fieldMetadataRepository: Repository<FieldMetadataEntity>,
|
||||
private readonly workspaceMetadataCacheService: WorkspaceMetadataCacheService,
|
||||
private readonly workspaceMigrationBuilderV2: WorkspaceMigrationBuilderV2Service,
|
||||
private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService,
|
||||
private readonly flatFieldMetadataValidatorService: FlatFieldMetadataValidatorService,
|
||||
private readonly workspaceMigrationRunnerV2Service: WorkspaceMigrationRunnerV2Service,
|
||||
) {}
|
||||
|
||||
async createOne({
|
||||
@@ -104,46 +102,42 @@ export class FieldMetadataServiceV2 {
|
||||
if (validationErrors.length > 0) {
|
||||
throw new MultipleMetadataValidationErrors(
|
||||
validationErrors,
|
||||
validationErrors.length > 1
|
||||
? 'Multiple validation errors occurred while deleting field'
|
||||
: 'A validation error occurred while deleting field',
|
||||
'Multiple validation errors occurred while deleting field',
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const flatObjectMetadataMapsWithImpactedObject =
|
||||
getSubFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
objectMetadataIds: flatFieldMetadatasToDelete.map(
|
||||
(flatFieldMetadataToDelete) =>
|
||||
flatFieldMetadataToDelete.objectMetadataId,
|
||||
),
|
||||
});
|
||||
|
||||
const toFlatObjectMetadataMaps = flatFieldMetadatasToDelete.reduce(
|
||||
(flatObjectMetadataMaps, flatFieldMetadataToDelete) =>
|
||||
deleteFieldFromFlatObjectMetadataMapsOrThrow({
|
||||
fieldMetadataId: flatFieldMetadataToDelete.id,
|
||||
flatObjectMetadataMaps,
|
||||
objectMetadataId: flatFieldMetadataToDelete.objectMetadataId,
|
||||
}),
|
||||
flatObjectMetadataMapsWithImpactedObject,
|
||||
);
|
||||
|
||||
const workspaceMigration = this.workspaceMigrationBuilderV2.build({
|
||||
fromFlatObjectMetadataMaps: flatObjectMetadataMapsWithImpactedObject,
|
||||
toFlatObjectMetadataMaps,
|
||||
inferDeletionFromMissingObjectFieldIndex: true,
|
||||
workspaceId,
|
||||
const flatObjectMetadataMapsWithImpactedObject =
|
||||
getSubFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
objectMetadataIds: flatFieldMetadatasToDelete.map(
|
||||
(flatFieldMetadataToDelete) =>
|
||||
flatFieldMetadataToDelete.objectMetadataId,
|
||||
),
|
||||
});
|
||||
|
||||
await this.workspaceMigrationRunnerV2Service.run(workspaceMigration);
|
||||
} catch {
|
||||
throw new FieldMetadataException(
|
||||
'Workspace migration failed to run',
|
||||
FieldMetadataExceptionCode.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const toFlatObjectMetadataMaps = flatFieldMetadatasToDelete.reduce(
|
||||
(flatObjectMetadataMaps, flatFieldMetadataToDelete) =>
|
||||
deleteFieldFromFlatObjectMetadataMapsOrThrow({
|
||||
fieldMetadataId: flatFieldMetadataToDelete.id,
|
||||
flatObjectMetadataMaps,
|
||||
objectMetadataId: flatFieldMetadataToDelete.objectMetadataId,
|
||||
}),
|
||||
flatObjectMetadataMapsWithImpactedObject,
|
||||
);
|
||||
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
||||
{
|
||||
buildOptions: {
|
||||
isSystemBuild: false,
|
||||
inferDeletionFromMissingObjectFieldIndex: true,
|
||||
},
|
||||
fromFlatObjectMetadataMaps: flatObjectMetadataMapsWithImpactedObject,
|
||||
toFlatObjectMetadataMaps,
|
||||
workspaceId,
|
||||
errorMessage:
|
||||
'Multiple validation errors occurred while deleting field',
|
||||
},
|
||||
);
|
||||
|
||||
return fromFlatFieldMetadataToFieldMetadataDto(
|
||||
flatFieldMetadatasToDelete[0],
|
||||
@@ -241,39 +235,38 @@ export class FieldMetadataServiceV2 {
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const fromFlatObjectMetadataMaps = getSubFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
objectMetadataIds: [
|
||||
optimisticiallyUpdatedFlatFieldMetadata.objectMetadataId,
|
||||
],
|
||||
const fromFlatObjectMetadataMaps = getSubFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
objectMetadataIds: [
|
||||
optimisticiallyUpdatedFlatFieldMetadata.objectMetadataId,
|
||||
],
|
||||
});
|
||||
const toFlatObjectMetadataMaps =
|
||||
replaceFlatFieldMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps: fromFlatObjectMetadataMaps,
|
||||
flatFieldMetadata: optimisticiallyUpdatedFlatFieldMetadata,
|
||||
});
|
||||
const toFlatObjectMetadataMaps =
|
||||
replaceFlatFieldMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps: fromFlatObjectMetadataMaps,
|
||||
flatFieldMetadata: optimisticiallyUpdatedFlatFieldMetadata,
|
||||
});
|
||||
const workspaceMigration = this.workspaceMigrationBuilderV2.build({
|
||||
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
||||
{
|
||||
fromFlatObjectMetadataMaps,
|
||||
toFlatObjectMetadataMaps,
|
||||
inferDeletionFromMissingObjectFieldIndex: false,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
await this.workspaceMigrationRunnerV2Service.run(workspaceMigration);
|
||||
|
||||
return this.fieldMetadataRepository.findOneOrFail({
|
||||
where: {
|
||||
id: optimisticiallyUpdatedFlatFieldMetadata.id,
|
||||
workspaceId,
|
||||
buildOptions: {
|
||||
isSystemBuild: false,
|
||||
inferDeletionFromMissingObjectFieldIndex: false,
|
||||
},
|
||||
});
|
||||
} catch {
|
||||
throw new FieldMetadataException(
|
||||
'Workspace migration failed to run',
|
||||
FieldMetadataExceptionCode.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
workspaceId,
|
||||
errorMessage:
|
||||
'Multiple validation errors occurred while updating field',
|
||||
},
|
||||
);
|
||||
|
||||
return this.fieldMetadataRepository.findOneOrFail({
|
||||
where: {
|
||||
id: optimisticiallyUpdatedFlatFieldMetadata.id,
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async createMany({
|
||||
@@ -371,43 +364,41 @@ export class FieldMetadataServiceV2 {
|
||||
),
|
||||
);
|
||||
|
||||
try {
|
||||
const fromFlatObjectMetadataMaps = getSubFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
objectMetadataIds: impactedObjectMetadataIds,
|
||||
});
|
||||
const toFlatObjectMetadataMaps = getSubFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps: optimisticFlatObjectMetadataMaps,
|
||||
objectMetadataIds: impactedObjectMetadataIds,
|
||||
});
|
||||
const workspaceMigration = this.workspaceMigrationBuilderV2.build({
|
||||
const fromFlatObjectMetadataMaps = getSubFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
objectMetadataIds: impactedObjectMetadataIds,
|
||||
});
|
||||
const toFlatObjectMetadataMaps = getSubFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps: optimisticFlatObjectMetadataMaps,
|
||||
objectMetadataIds: impactedObjectMetadataIds,
|
||||
});
|
||||
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
||||
{
|
||||
fromFlatObjectMetadataMaps,
|
||||
toFlatObjectMetadataMaps,
|
||||
inferDeletionFromMissingObjectFieldIndex: false,
|
||||
buildOptions: {
|
||||
isSystemBuild: false,
|
||||
inferDeletionFromMissingObjectFieldIndex: false,
|
||||
},
|
||||
workspaceId,
|
||||
});
|
||||
errorMessage:
|
||||
'Multiple validation errors occurred while creating fields',
|
||||
},
|
||||
);
|
||||
|
||||
await this.workspaceMigrationRunnerV2Service.run(workspaceMigration);
|
||||
|
||||
// In the best of the world could consume runner returned value instead of searching in db here
|
||||
return this.fieldMetadataRepository.find({
|
||||
where: {
|
||||
name: In(
|
||||
fieldMetadataInputs.map((flatFieldMetadata) =>
|
||||
trimAndRemoveDuplicatedWhitespacesFromString(
|
||||
flatFieldMetadata.name,
|
||||
),
|
||||
// In the best of the world could consume runner returned value instead of searching in db here
|
||||
return this.fieldMetadataRepository.find({
|
||||
where: {
|
||||
name: In(
|
||||
fieldMetadataInputs.map((flatFieldMetadata) =>
|
||||
trimAndRemoveDuplicatedWhitespacesFromString(
|
||||
flatFieldMetadata.name,
|
||||
),
|
||||
),
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
} catch {
|
||||
// TODO prastoin We should pass the internal error here
|
||||
throw new FieldMetadataException(
|
||||
'Workspace migration failed to run',
|
||||
FieldMetadataExceptionCode.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
),
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+864
@@ -0,0 +1,864 @@
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { DateDisplayFormat } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-settings.interface';
|
||||
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
|
||||
|
||||
import { getFlatFieldMetadataMock } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/get-flat-field-metadata.mock';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { RelationOnDeleteAction } from 'src/engine/metadata-modules/relation-metadata/relation-on-delete-action.type';
|
||||
|
||||
export const FAVORITE_FLAT_FIELDS_MOCK = {
|
||||
position: getFlatFieldMetadataMock({
|
||||
id: '64dbf3ef-a352-44bb-9055-f95822f18d0a',
|
||||
standardId: '20202020-dd26-42c6-8c3c-2a7598c204f6',
|
||||
objectMetadataId: 'f4749ffb-dde8-44ff-8b01-d3fc82df0ba2',
|
||||
type: FieldMetadataType.NUMBER,
|
||||
name: 'position',
|
||||
label: 'Position',
|
||||
defaultValue: 0,
|
||||
description: 'Favorite position',
|
||||
icon: 'IconList',
|
||||
standardOverrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
isNullable: false,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: true,
|
||||
relationTargetFieldMetadataId: null,
|
||||
relationTargetObjectMetadataId: null,
|
||||
uniqueIdentifier: '20202020-dd26-42c6-8c3c-2a7598c204f6',
|
||||
flatRelationTargetFieldMetadata: null,
|
||||
}),
|
||||
id: getFlatFieldMetadataMock({
|
||||
id: '49f58497-4e7c-49e6-b3bc-c91d4b8a8ddc',
|
||||
standardId: '20202020-eda0-4cee-9577-3eb357e3c22b',
|
||||
objectMetadataId: 'f4749ffb-dde8-44ff-8b01-d3fc82df0ba2',
|
||||
type: FieldMetadataType.UUID,
|
||||
name: 'id',
|
||||
label: 'Id',
|
||||
defaultValue: 'uuid',
|
||||
description: 'Id',
|
||||
icon: 'Icon123',
|
||||
standardOverrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
isNullable: false,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: true,
|
||||
relationTargetFieldMetadataId: null,
|
||||
relationTargetObjectMetadataId: null,
|
||||
uniqueIdentifier: '20202020-eda0-4cee-9577-3eb357e3c22b',
|
||||
flatRelationTargetFieldMetadata: null,
|
||||
}),
|
||||
createdAt: getFlatFieldMetadataMock({
|
||||
id: '0606a76c-8a72-48ca-90c6-526c729c25eb',
|
||||
standardId: '20202020-66ac-4502-9975-e4d959c50311',
|
||||
objectMetadataId: 'f4749ffb-dde8-44ff-8b01-d3fc82df0ba2',
|
||||
type: FieldMetadataType.DATE_TIME,
|
||||
name: 'createdAt',
|
||||
label: 'Creation date',
|
||||
defaultValue: 'now',
|
||||
description: 'Creation date',
|
||||
icon: 'IconCalendar',
|
||||
standardOverrides: null,
|
||||
options: null,
|
||||
settings: { displayFormat: DateDisplayFormat.RELATIVE },
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: false,
|
||||
isNullable: false,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: false,
|
||||
relationTargetFieldMetadataId: null,
|
||||
relationTargetObjectMetadataId: null,
|
||||
uniqueIdentifier: '20202020-66ac-4502-9975-e4d959c50311',
|
||||
flatRelationTargetFieldMetadata: null,
|
||||
}),
|
||||
updatedAt: getFlatFieldMetadataMock({
|
||||
id: 'fb202982-0c8e-4917-8dda-acb8248be749',
|
||||
standardId: '20202020-d767-4622-bdcf-d8a084834d86',
|
||||
objectMetadataId: 'f4749ffb-dde8-44ff-8b01-d3fc82df0ba2',
|
||||
type: FieldMetadataType.DATE_TIME,
|
||||
name: 'updatedAt',
|
||||
label: 'Last update',
|
||||
defaultValue: 'now',
|
||||
description: 'Last time the record was changed',
|
||||
icon: 'IconCalendarClock',
|
||||
standardOverrides: null,
|
||||
options: null,
|
||||
settings: { displayFormat: DateDisplayFormat.RELATIVE },
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: false,
|
||||
isNullable: false,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: false,
|
||||
relationTargetFieldMetadataId: null,
|
||||
relationTargetObjectMetadataId: null,
|
||||
uniqueIdentifier: '20202020-d767-4622-bdcf-d8a084834d86',
|
||||
flatRelationTargetFieldMetadata: null,
|
||||
}),
|
||||
deletedAt: getFlatFieldMetadataMock({
|
||||
id: 'ef52e151-a974-470f-bbe7-f345fd4eca65',
|
||||
standardId: '20202020-b9a7-48d8-8387-b9a3090a50ec',
|
||||
objectMetadataId: 'f4749ffb-dde8-44ff-8b01-d3fc82df0ba2',
|
||||
type: FieldMetadataType.DATE_TIME,
|
||||
name: 'deletedAt',
|
||||
label: 'Deleted at',
|
||||
defaultValue: null,
|
||||
description: 'Date when the record was deleted',
|
||||
icon: 'IconCalendarMinus',
|
||||
standardOverrides: null,
|
||||
options: null,
|
||||
settings: { displayFormat: DateDisplayFormat.RELATIVE },
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: false,
|
||||
isNullable: true,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: true,
|
||||
relationTargetFieldMetadataId: null,
|
||||
relationTargetObjectMetadataId: null,
|
||||
uniqueIdentifier: '20202020-b9a7-48d8-8387-b9a3090a50ec',
|
||||
flatRelationTargetFieldMetadata: null,
|
||||
}),
|
||||
forWorkspaceMember: getFlatFieldMetadataMock({
|
||||
id: '07f2c3ba-63d1-4e44-bac4-bc4591e51bac',
|
||||
standardId: '20202020-ce63-49cb-9676-fdc0c45892cd',
|
||||
objectMetadataId: 'f4749ffb-dde8-44ff-8b01-d3fc82df0ba2',
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'forWorkspaceMember',
|
||||
label: 'Workspace Member',
|
||||
defaultValue: null,
|
||||
description: 'Favorite workspace member',
|
||||
icon: 'IconCircleUser',
|
||||
standardOverrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
relationType: RelationType.MANY_TO_ONE,
|
||||
joinColumnName: 'forWorkspaceMemberId',
|
||||
},
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
isNullable: true,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: false,
|
||||
relationTargetFieldMetadataId: 'a15a4b41-1849-4dea-aa43-f53caad016be',
|
||||
relationTargetObjectMetadataId: '7d8264db-2b55-4b74-81ff-b7064ed43840',
|
||||
uniqueIdentifier: '20202020-ce63-49cb-9676-fdc0c45892cd',
|
||||
flatRelationTargetFieldMetadata: getFlatFieldMetadataMock({
|
||||
id: 'a15a4b41-1849-4dea-aa43-f53caad016be',
|
||||
standardId: '20202020-f3c1-4faf-b343-cf7681038757',
|
||||
objectMetadataId: '7d8264db-2b55-4b74-81ff-b7064ed43840',
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'favorites',
|
||||
label: 'Favorites',
|
||||
defaultValue: null,
|
||||
description: 'Favorites linked to the workspace member',
|
||||
icon: 'IconHeart',
|
||||
standardOverrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
isNullable: true,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: true,
|
||||
relationTargetFieldMetadataId: '07f2c3ba-63d1-4e44-bac4-bc4591e51bac',
|
||||
relationTargetObjectMetadataId: 'f4749ffb-dde8-44ff-8b01-d3fc82df0ba2',
|
||||
uniqueIdentifier: '20202020-f3c1-4faf-b343-cf7681038757',
|
||||
}),
|
||||
}),
|
||||
person: getFlatFieldMetadataMock({
|
||||
id: '013cec20-e9cc-42db-b9b9-ff6bd0f52abd',
|
||||
standardId: '20202020-c428-4f40-b6f3-86091511c41c',
|
||||
objectMetadataId: 'f4749ffb-dde8-44ff-8b01-d3fc82df0ba2',
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'person',
|
||||
label: 'Person',
|
||||
defaultValue: null,
|
||||
description: 'Favorite person',
|
||||
icon: 'IconUser',
|
||||
standardOverrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
relationType: RelationType.MANY_TO_ONE,
|
||||
joinColumnName: 'personId',
|
||||
},
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
isNullable: true,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: true,
|
||||
relationTargetFieldMetadataId: 'a1a72bee-1c15-4598-ba23-67ba8e3e505d',
|
||||
relationTargetObjectMetadataId: '843e0b67-9619-4628-91c4-2fa62256a611',
|
||||
uniqueIdentifier: '20202020-c428-4f40-b6f3-86091511c41c',
|
||||
flatRelationTargetFieldMetadata: getFlatFieldMetadataMock({
|
||||
id: 'a1a72bee-1c15-4598-ba23-67ba8e3e505d',
|
||||
standardId: '20202020-4073-4117-9cf1-203bcdc91cbd',
|
||||
objectMetadataId: '843e0b67-9619-4628-91c4-2fa62256a611',
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'favorites',
|
||||
label: 'Favorites',
|
||||
defaultValue: null,
|
||||
description: 'Favorites linked to the contact',
|
||||
icon: 'IconHeart',
|
||||
standardOverrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
isNullable: true,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: true,
|
||||
relationTargetFieldMetadataId: '013cec20-e9cc-42db-b9b9-ff6bd0f52abd',
|
||||
relationTargetObjectMetadataId: 'f4749ffb-dde8-44ff-8b01-d3fc82df0ba2',
|
||||
uniqueIdentifier: '20202020-4073-4117-9cf1-203bcdc91cbd',
|
||||
}),
|
||||
}),
|
||||
company: getFlatFieldMetadataMock({
|
||||
id: '9a11532c-6839-46ed-a2f5-ed03f88b86f6',
|
||||
standardId: '20202020-cff5-4682-8bf9-069169e08279',
|
||||
objectMetadataId: 'f4749ffb-dde8-44ff-8b01-d3fc82df0ba2',
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'company',
|
||||
label: 'Company',
|
||||
defaultValue: null,
|
||||
description: 'Favorite company',
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
standardOverrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
relationType: RelationType.MANY_TO_ONE,
|
||||
joinColumnName: 'companyId',
|
||||
},
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
isNullable: true,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: true,
|
||||
relationTargetFieldMetadataId: '72415216-fed4-450b-aa00-691e37718b01',
|
||||
relationTargetObjectMetadataId: '7f5c2c7a-bb23-46fb-b59d-9b7a52a8d1cc',
|
||||
uniqueIdentifier: '20202020-cff5-4682-8bf9-069169e08279',
|
||||
flatRelationTargetFieldMetadata: getFlatFieldMetadataMock({
|
||||
id: '72415216-fed4-450b-aa00-691e37718b01',
|
||||
standardId: '20202020-4d1d-41ac-b13b-621631298d55',
|
||||
objectMetadataId: '7f5c2c7a-bb23-46fb-b59d-9b7a52a8d1cc',
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'favorites',
|
||||
label: 'Favorites',
|
||||
defaultValue: null,
|
||||
description: 'Favorites linked to the company',
|
||||
icon: 'IconHeart',
|
||||
standardOverrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
isNullable: true,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: true,
|
||||
relationTargetFieldMetadataId: '9a11532c-6839-46ed-a2f5-ed03f88b86f6',
|
||||
relationTargetObjectMetadataId: 'f4749ffb-dde8-44ff-8b01-d3fc82df0ba2',
|
||||
uniqueIdentifier: '20202020-4d1d-41ac-b13b-621631298d55',
|
||||
}),
|
||||
}),
|
||||
favoriteFolder: getFlatFieldMetadataMock({
|
||||
id: 'fabbe669-b8fc-49fe-8010-f13d01eb61f1',
|
||||
standardId: '20202020-f658-4d12-8b4d-248356aa4bd9',
|
||||
objectMetadataId: 'f4749ffb-dde8-44ff-8b01-d3fc82df0ba2',
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'favoriteFolder',
|
||||
label: 'Favorite Folder',
|
||||
defaultValue: null,
|
||||
description: 'The folder this favorite belongs to',
|
||||
icon: 'IconFolder',
|
||||
standardOverrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.SET_NULL,
|
||||
relationType: RelationType.MANY_TO_ONE,
|
||||
joinColumnName: 'favoriteFolderId',
|
||||
},
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
isNullable: true,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: true,
|
||||
relationTargetFieldMetadataId: '71b95076-6b8d-45c9-9a7c-ab981b81bd1b',
|
||||
relationTargetObjectMetadataId: '35763b74-1abe-4c4b-9eab-27693f0ee06d',
|
||||
uniqueIdentifier: '20202020-f658-4d12-8b4d-248356aa4bd9',
|
||||
flatRelationTargetFieldMetadata: getFlatFieldMetadataMock({
|
||||
id: '71b95076-6b8d-45c9-9a7c-ab981b81bd1b',
|
||||
standardId: '20202020-b5e3-4b42-8af2-03cd4fd2e4d2',
|
||||
objectMetadataId: '35763b74-1abe-4c4b-9eab-27693f0ee06d',
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'favorites',
|
||||
label: 'Favorites',
|
||||
defaultValue: null,
|
||||
description: 'Favorites in this folder',
|
||||
icon: 'IconHeart',
|
||||
standardOverrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
isNullable: true,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: true,
|
||||
relationTargetFieldMetadataId: 'fabbe669-b8fc-49fe-8010-f13d01eb61f1',
|
||||
relationTargetObjectMetadataId: 'f4749ffb-dde8-44ff-8b01-d3fc82df0ba2',
|
||||
uniqueIdentifier: '20202020-b5e3-4b42-8af2-03cd4fd2e4d2',
|
||||
}),
|
||||
}),
|
||||
opportunity: getFlatFieldMetadataMock({
|
||||
id: '6380cf8e-4327-4d1e-ad21-1826ce4ecf05',
|
||||
standardId: '20202020-dabc-48e1-8318-2781a2b32aa2',
|
||||
objectMetadataId: 'f4749ffb-dde8-44ff-8b01-d3fc82df0ba2',
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'opportunity',
|
||||
label: 'Opportunity',
|
||||
defaultValue: null,
|
||||
description: 'Favorite opportunity',
|
||||
icon: 'IconTargetArrow',
|
||||
standardOverrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
relationType: RelationType.MANY_TO_ONE,
|
||||
joinColumnName: 'opportunityId',
|
||||
},
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
isNullable: true,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: true,
|
||||
relationTargetFieldMetadataId: '87bb9924-a1cc-4357-9c8a-52141dabf266',
|
||||
relationTargetObjectMetadataId: 'e6996bbf-dd41-423a-9324-8546f5b22fa7',
|
||||
uniqueIdentifier: '20202020-dabc-48e1-8318-2781a2b32aa2',
|
||||
flatRelationTargetFieldMetadata: getFlatFieldMetadataMock({
|
||||
id: '87bb9924-a1cc-4357-9c8a-52141dabf266',
|
||||
standardId: '20202020-a1c2-4500-aaae-83ba8a0e827a',
|
||||
objectMetadataId: 'e6996bbf-dd41-423a-9324-8546f5b22fa7',
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'favorites',
|
||||
label: 'Favorites',
|
||||
defaultValue: null,
|
||||
description: 'Favorites linked to the opportunity',
|
||||
icon: 'IconHeart',
|
||||
standardOverrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
isNullable: true,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: true,
|
||||
relationTargetFieldMetadataId: '6380cf8e-4327-4d1e-ad21-1826ce4ecf05',
|
||||
relationTargetObjectMetadataId: 'f4749ffb-dde8-44ff-8b01-d3fc82df0ba2',
|
||||
uniqueIdentifier: '20202020-a1c2-4500-aaae-83ba8a0e827a',
|
||||
}),
|
||||
}),
|
||||
workflow: getFlatFieldMetadataMock({
|
||||
id: '12c20527-9b1d-4922-9650-00cc31b26387',
|
||||
standardId: '20202020-b11b-4dc8-999a-6bd0a947b463',
|
||||
objectMetadataId: 'f4749ffb-dde8-44ff-8b01-d3fc82df0ba2',
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'workflow',
|
||||
label: 'Workflow',
|
||||
defaultValue: null,
|
||||
description: 'Favorite workflow',
|
||||
icon: 'IconSettingsAutomation',
|
||||
standardOverrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
relationType: RelationType.MANY_TO_ONE,
|
||||
joinColumnName: 'workflowId',
|
||||
},
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
isNullable: true,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: true,
|
||||
relationTargetFieldMetadataId: 'b848abe8-276b-46c9-9eab-baf34d2a0583',
|
||||
relationTargetObjectMetadataId: '664c19a5-0693-45d4-a4e5-76c3717db83c',
|
||||
uniqueIdentifier: '20202020-b11b-4dc8-999a-6bd0a947b463',
|
||||
flatRelationTargetFieldMetadata: getFlatFieldMetadataMock({
|
||||
id: 'b848abe8-276b-46c9-9eab-baf34d2a0583',
|
||||
standardId: '20202020-c554-4c41-be7a-cf9cd4b0d512',
|
||||
objectMetadataId: '664c19a5-0693-45d4-a4e5-76c3717db83c',
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'favorites',
|
||||
label: 'Favorites',
|
||||
defaultValue: null,
|
||||
description: 'Favorites linked to the workflow',
|
||||
icon: 'IconHeart',
|
||||
standardOverrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
isNullable: true,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: true,
|
||||
relationTargetFieldMetadataId: '12c20527-9b1d-4922-9650-00cc31b26387',
|
||||
relationTargetObjectMetadataId: 'f4749ffb-dde8-44ff-8b01-d3fc82df0ba2',
|
||||
uniqueIdentifier: '20202020-c554-4c41-be7a-cf9cd4b0d512',
|
||||
}),
|
||||
}),
|
||||
workflowVersion: getFlatFieldMetadataMock({
|
||||
id: '5cb92f25-c067-4e19-afb3-c7cc038ed1a4',
|
||||
standardId: '20202020-e1b8-4caf-b55a-3ab4d4cbcd21',
|
||||
objectMetadataId: 'f4749ffb-dde8-44ff-8b01-d3fc82df0ba2',
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'workflowVersion',
|
||||
label: 'Workflow',
|
||||
defaultValue: null,
|
||||
description: 'Favorite workflow version',
|
||||
icon: 'IconSettingsAutomation',
|
||||
standardOverrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
relationType: RelationType.MANY_TO_ONE,
|
||||
joinColumnName: 'workflowVersionId',
|
||||
},
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
isNullable: true,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: false,
|
||||
relationTargetFieldMetadataId: 'ea6b6bda-6ba7-4549-b81a-1f3ced0d4b25',
|
||||
relationTargetObjectMetadataId: '99cf5f27-b41d-4f25-a23b-9e47d27dae9b',
|
||||
uniqueIdentifier: '20202020-e1b8-4caf-b55a-3ab4d4cbcd21',
|
||||
flatRelationTargetFieldMetadata: getFlatFieldMetadataMock({
|
||||
id: 'ea6b6bda-6ba7-4549-b81a-1f3ced0d4b25',
|
||||
standardId: '20202020-b8e0-4e57-928d-b51671cc71f2',
|
||||
objectMetadataId: '99cf5f27-b41d-4f25-a23b-9e47d27dae9b',
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'favorites',
|
||||
label: 'Favorites',
|
||||
defaultValue: null,
|
||||
description: 'Favorites linked to the workflow version',
|
||||
icon: 'IconHeart',
|
||||
standardOverrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
isNullable: true,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: true,
|
||||
relationTargetFieldMetadataId: '5cb92f25-c067-4e19-afb3-c7cc038ed1a4',
|
||||
relationTargetObjectMetadataId: 'f4749ffb-dde8-44ff-8b01-d3fc82df0ba2',
|
||||
uniqueIdentifier: '20202020-b8e0-4e57-928d-b51671cc71f2',
|
||||
}),
|
||||
}),
|
||||
workflowRun: getFlatFieldMetadataMock({
|
||||
id: 'a4bdd0d8-c36f-4519-812b-be4800ab14b0',
|
||||
standardId: '20202020-db5a-4fe4-9a13-9afa22b1e762',
|
||||
objectMetadataId: 'f4749ffb-dde8-44ff-8b01-d3fc82df0ba2',
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'workflowRun',
|
||||
label: 'Workflow',
|
||||
defaultValue: null,
|
||||
description: 'Favorite workflow run',
|
||||
icon: 'IconSettingsAutomation',
|
||||
standardOverrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
relationType: RelationType.MANY_TO_ONE,
|
||||
joinColumnName: 'workflowRunId',
|
||||
},
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
isNullable: true,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: false,
|
||||
relationTargetFieldMetadataId: '444a6e9e-443d-4f04-a60d-32f76f17049c',
|
||||
relationTargetObjectMetadataId: '06d95e96-b050-443f-946e-dd276d291a2f',
|
||||
uniqueIdentifier: '20202020-db5a-4fe4-9a13-9afa22b1e762',
|
||||
flatRelationTargetFieldMetadata: getFlatFieldMetadataMock({
|
||||
id: '444a6e9e-443d-4f04-a60d-32f76f17049c',
|
||||
standardId: '20202020-4baf-4604-b899-2f7fcfbbf90d',
|
||||
objectMetadataId: '06d95e96-b050-443f-946e-dd276d291a2f',
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'favorites',
|
||||
label: 'Favorites',
|
||||
defaultValue: null,
|
||||
description: 'Favorites linked to the workflow run',
|
||||
icon: 'IconHeart',
|
||||
standardOverrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
isNullable: true,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: true,
|
||||
relationTargetFieldMetadataId: 'a4bdd0d8-c36f-4519-812b-be4800ab14b0',
|
||||
relationTargetObjectMetadataId: 'f4749ffb-dde8-44ff-8b01-d3fc82df0ba2',
|
||||
uniqueIdentifier: '20202020-4baf-4604-b899-2f7fcfbbf90d',
|
||||
}),
|
||||
}),
|
||||
task: getFlatFieldMetadataMock({
|
||||
id: 'db16ab78-ea3f-4b67-93e3-6ad9ab1b7273',
|
||||
standardId: '20202020-1b1b-4b3b-8b1b-7f8d6a1d7d5c',
|
||||
objectMetadataId: 'f4749ffb-dde8-44ff-8b01-d3fc82df0ba2',
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'task',
|
||||
label: 'Task',
|
||||
defaultValue: null,
|
||||
description: 'Favorite task',
|
||||
icon: 'IconCheckbox',
|
||||
standardOverrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
relationType: RelationType.MANY_TO_ONE,
|
||||
joinColumnName: 'taskId',
|
||||
},
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
isNullable: true,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: true,
|
||||
relationTargetFieldMetadataId: '33e4eff0-ff0f-4530-802a-cd630481ee84',
|
||||
relationTargetObjectMetadataId: '3186920d-2a15-4b5f-96c7-2bf6567024b0',
|
||||
uniqueIdentifier: '20202020-1b1b-4b3b-8b1b-7f8d6a1d7d5c',
|
||||
flatRelationTargetFieldMetadata: getFlatFieldMetadataMock({
|
||||
id: '33e4eff0-ff0f-4530-802a-cd630481ee84',
|
||||
standardId: '20202020-4d1d-41ac-b13b-621631298d65',
|
||||
objectMetadataId: '3186920d-2a15-4b5f-96c7-2bf6567024b0',
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'favorites',
|
||||
label: 'Favorites',
|
||||
defaultValue: null,
|
||||
description: 'Favorites linked to the task',
|
||||
icon: 'IconHeart',
|
||||
standardOverrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
isNullable: true,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: true,
|
||||
relationTargetFieldMetadataId: 'db16ab78-ea3f-4b67-93e3-6ad9ab1b7273',
|
||||
relationTargetObjectMetadataId: 'f4749ffb-dde8-44ff-8b01-d3fc82df0ba2',
|
||||
uniqueIdentifier: '20202020-4d1d-41ac-b13b-621631298d65',
|
||||
}),
|
||||
}),
|
||||
note: getFlatFieldMetadataMock({
|
||||
id: 'a61baf0f-2d43-4fda-9baf-efb000c17ecd',
|
||||
standardId: '20202020-1f25-43fe-8b00-af212fdde824',
|
||||
objectMetadataId: 'f4749ffb-dde8-44ff-8b01-d3fc82df0ba2',
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'note',
|
||||
label: 'Note',
|
||||
defaultValue: null,
|
||||
description: 'Favorite note',
|
||||
icon: 'IconNotes',
|
||||
standardOverrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
relationType: RelationType.MANY_TO_ONE,
|
||||
joinColumnName: 'noteId',
|
||||
},
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
isNullable: true,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: true,
|
||||
relationTargetFieldMetadataId: '065a1ede-f773-48ca-84bf-70f61d838396',
|
||||
relationTargetObjectMetadataId: '1253e3e5-5b00-4a34-93b8-02f3dc6e2b7c',
|
||||
uniqueIdentifier: '20202020-1f25-43fe-8b00-af212fdde824',
|
||||
flatRelationTargetFieldMetadata: getFlatFieldMetadataMock({
|
||||
id: '065a1ede-f773-48ca-84bf-70f61d838396',
|
||||
standardId: '20202020-4d1d-41ac-b13b-621631298d67',
|
||||
objectMetadataId: '1253e3e5-5b00-4a34-93b8-02f3dc6e2b7c',
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'favorites',
|
||||
label: 'Favorites',
|
||||
defaultValue: null,
|
||||
description: 'Favorites linked to the note',
|
||||
icon: 'IconHeart',
|
||||
standardOverrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
isNullable: true,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: true,
|
||||
relationTargetFieldMetadataId: 'a61baf0f-2d43-4fda-9baf-efb000c17ecd',
|
||||
relationTargetObjectMetadataId: 'f4749ffb-dde8-44ff-8b01-d3fc82df0ba2',
|
||||
uniqueIdentifier: '20202020-4d1d-41ac-b13b-621631298d67',
|
||||
}),
|
||||
}),
|
||||
view: getFlatFieldMetadataMock({
|
||||
id: 'accd216c-790e-4bd2-83bb-0700e06d0e5c',
|
||||
standardId: '20202020-5a93-4fa9-acce-e73481a0bbdf',
|
||||
objectMetadataId: 'f4749ffb-dde8-44ff-8b01-d3fc82df0ba2',
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'view',
|
||||
label: 'View',
|
||||
defaultValue: null,
|
||||
description: 'Favorite view',
|
||||
icon: 'IconLayoutCollage',
|
||||
standardOverrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
relationType: RelationType.MANY_TO_ONE,
|
||||
joinColumnName: 'viewId',
|
||||
},
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
isNullable: true,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: true,
|
||||
relationTargetFieldMetadataId: '90a103ec-7dce-4bd4-aed4-e2c9d46ecf32',
|
||||
relationTargetObjectMetadataId: '8377fc0d-ec1c-4fb5-b6ae-9381d9b22a95',
|
||||
uniqueIdentifier: '20202020-5a93-4fa9-acce-e73481a0bbdf',
|
||||
flatRelationTargetFieldMetadata: getFlatFieldMetadataMock({
|
||||
id: '90a103ec-7dce-4bd4-aed4-e2c9d46ecf32',
|
||||
standardId: '20202020-c818-4a86-8284-9ec0ef0a59a5',
|
||||
objectMetadataId: '8377fc0d-ec1c-4fb5-b6ae-9381d9b22a95',
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'favorites',
|
||||
label: 'Favorites',
|
||||
defaultValue: null,
|
||||
description: 'Favorites linked to the view',
|
||||
icon: 'IconHeart',
|
||||
standardOverrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
isNullable: true,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: true,
|
||||
relationTargetFieldMetadataId: 'accd216c-790e-4bd2-83bb-0700e06d0e5c',
|
||||
relationTargetObjectMetadataId: 'f4749ffb-dde8-44ff-8b01-d3fc82df0ba2',
|
||||
uniqueIdentifier: '20202020-c818-4a86-8284-9ec0ef0a59a5',
|
||||
}),
|
||||
}),
|
||||
rocket: getFlatFieldMetadataMock({
|
||||
id: '9448627d-725d-499e-a071-90fd5cfcf0ea',
|
||||
standardId: '20202020-78f7-4d7d-86e9-7562ced0b381',
|
||||
objectMetadataId: 'f4749ffb-dde8-44ff-8b01-d3fc82df0ba2',
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'rocket',
|
||||
label: 'Rocket',
|
||||
defaultValue: null,
|
||||
description: 'Favorites Rocket',
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
standardOverrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
relationType: RelationType.MANY_TO_ONE,
|
||||
joinColumnName: 'rocketId',
|
||||
},
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
isNullable: true,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: false,
|
||||
relationTargetFieldMetadataId: '14010360-f3c2-442e-b05f-43c30158f246',
|
||||
relationTargetObjectMetadataId: 'd78ec657-74a4-4652-a350-1f44ff62970a',
|
||||
uniqueIdentifier: '20202020-78f7-4d7d-86e9-7562ced0b381',
|
||||
flatRelationTargetFieldMetadata: getFlatFieldMetadataMock({
|
||||
id: '14010360-f3c2-442e-b05f-43c30158f246',
|
||||
standardId: '20202020-a4a7-4686-b296-1c6c3482ee21',
|
||||
objectMetadataId: 'd78ec657-74a4-4652-a350-1f44ff62970a',
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'favorites',
|
||||
label: 'Favorites',
|
||||
defaultValue: null,
|
||||
description: 'Favorites tied to the Rocket',
|
||||
icon: 'IconHeart',
|
||||
standardOverrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
isNullable: true,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: false,
|
||||
relationTargetFieldMetadataId: '9448627d-725d-499e-a071-90fd5cfcf0ea',
|
||||
relationTargetObjectMetadataId: 'f4749ffb-dde8-44ff-8b01-d3fc82df0ba2',
|
||||
uniqueIdentifier: '20202020-a4a7-4686-b296-1c6c3482ee21',
|
||||
}),
|
||||
}),
|
||||
pet: getFlatFieldMetadataMock({
|
||||
id: 'd34b4048-bf0c-4c61-8ffe-59f1838a2cc4',
|
||||
standardId: '20202020-1bd7-4d10-80dc-968b9f5d1a8a',
|
||||
objectMetadataId: 'f4749ffb-dde8-44ff-8b01-d3fc82df0ba2',
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'pet',
|
||||
label: 'Pet',
|
||||
defaultValue: null,
|
||||
description: 'Favorites Pet',
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
standardOverrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
relationType: RelationType.MANY_TO_ONE,
|
||||
joinColumnName: 'petId',
|
||||
},
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
isNullable: true,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: false,
|
||||
relationTargetFieldMetadataId: '80bc4e7a-6bd1-4f2b-98b1-6333c631cc1a',
|
||||
relationTargetObjectMetadataId: 'd34e0f07-1b8c-4de0-938e-599cf05e1f7f',
|
||||
uniqueIdentifier: '20202020-1bd7-4d10-80dc-968b9f5d1a8a',
|
||||
flatRelationTargetFieldMetadata: getFlatFieldMetadataMock({
|
||||
id: '80bc4e7a-6bd1-4f2b-98b1-6333c631cc1a',
|
||||
standardId: '20202020-a4a7-4686-b296-1c6c3482ee21',
|
||||
objectMetadataId: 'd34e0f07-1b8c-4de0-938e-599cf05e1f7f',
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'favorites',
|
||||
label: 'Favorites',
|
||||
defaultValue: null,
|
||||
description: 'Favorites tied to the Pet',
|
||||
icon: 'IconHeart',
|
||||
standardOverrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
isNullable: true,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: false,
|
||||
relationTargetFieldMetadataId: 'd34b4048-bf0c-4c61-8ffe-59f1838a2cc4',
|
||||
relationTargetObjectMetadataId: 'f4749ffb-dde8-44ff-8b01-d3fc82df0ba2',
|
||||
uniqueIdentifier: '20202020-a4a7-4686-b296-1c6c3482ee21',
|
||||
}),
|
||||
}),
|
||||
surveyResult: getFlatFieldMetadataMock({
|
||||
id: '9cdfb5a1-bb9c-4a59-811d-f5a1034a8f8b',
|
||||
standardId: '20202020-d049-4912-80ee-43b09fe3b0ac',
|
||||
objectMetadataId: 'f4749ffb-dde8-44ff-8b01-d3fc82df0ba2',
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'surveyResult',
|
||||
label: 'Survey result',
|
||||
defaultValue: null,
|
||||
description: 'Favorites Survey result',
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
standardOverrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.CASCADE,
|
||||
relationType: RelationType.MANY_TO_ONE,
|
||||
joinColumnName: 'surveyResultId',
|
||||
},
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
isNullable: true,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: false,
|
||||
relationTargetFieldMetadataId: '8e11aced-3a20-4551-baa1-b38b98a74e1c',
|
||||
relationTargetObjectMetadataId: '713da753-0340-49b5-b1fa-add34d2dc9a8',
|
||||
uniqueIdentifier: '20202020-d049-4912-80ee-43b09fe3b0ac',
|
||||
flatRelationTargetFieldMetadata: getFlatFieldMetadataMock({
|
||||
id: '8e11aced-3a20-4551-baa1-b38b98a74e1c',
|
||||
standardId: '20202020-a4a7-4686-b296-1c6c3482ee21',
|
||||
objectMetadataId: '713da753-0340-49b5-b1fa-add34d2dc9a8',
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'favorites',
|
||||
label: 'Favorites',
|
||||
defaultValue: null,
|
||||
description: 'Favorites tied to the Survey result',
|
||||
icon: 'IconHeart',
|
||||
standardOverrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
isNullable: true,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: false,
|
||||
relationTargetFieldMetadataId: '9cdfb5a1-bb9c-4a59-811d-f5a1034a8f8b',
|
||||
relationTargetObjectMetadataId: 'f4749ffb-dde8-44ff-8b01-d3fc82df0ba2',
|
||||
uniqueIdentifier: '20202020-a4a7-4686-b296-1c6c3482ee21',
|
||||
}),
|
||||
}),
|
||||
} as const satisfies Record<string, FlatFieldMetadata>;
|
||||
+213
@@ -0,0 +1,213 @@
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { DateDisplayFormat } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-settings.interface';
|
||||
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
|
||||
|
||||
import { getFlatFieldMetadataMock } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/get-flat-field-metadata.mock';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { RelationOnDeleteAction } from 'src/engine/metadata-modules/relation-metadata/relation-on-delete-action.type';
|
||||
|
||||
export const FAVORITE_FOLDER_FLAT_FIELDS_MOCK = {
|
||||
position: getFlatFieldMetadataMock({
|
||||
id: '5894ab56-99b7-49e8-98be-dd6524250df5',
|
||||
standardId: '20202020-5278-4bde-8909-2cec74d43744',
|
||||
objectMetadataId: '35763b74-1abe-4c4b-9eab-27693f0ee06d',
|
||||
type: FieldMetadataType.NUMBER,
|
||||
name: 'position',
|
||||
label: 'Position',
|
||||
defaultValue: 0,
|
||||
description: 'Favorite folder position',
|
||||
icon: 'IconList',
|
||||
standardOverrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
isNullable: false,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: true,
|
||||
relationTargetFieldMetadataId: null,
|
||||
relationTargetObjectMetadataId: null,
|
||||
uniqueIdentifier: '20202020-5278-4bde-8909-2cec74d43744',
|
||||
flatRelationTargetFieldMetadata: null,
|
||||
}),
|
||||
name: getFlatFieldMetadataMock({
|
||||
id: '1502abe0-b2f8-43a7-ae55-91c826b34ffe',
|
||||
standardId: '20202020-82a3-4537-8ff0-dbce7eec35d6',
|
||||
objectMetadataId: '35763b74-1abe-4c4b-9eab-27693f0ee06d',
|
||||
type: FieldMetadataType.TEXT,
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
defaultValue: "''",
|
||||
description: 'Name of the favorite folder',
|
||||
icon: 'IconText',
|
||||
standardOverrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: false,
|
||||
isNullable: false,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: true,
|
||||
relationTargetFieldMetadataId: null,
|
||||
relationTargetObjectMetadataId: null,
|
||||
uniqueIdentifier: '20202020-82a3-4537-8ff0-dbce7eec35d6',
|
||||
flatRelationTargetFieldMetadata: null,
|
||||
}),
|
||||
id: getFlatFieldMetadataMock({
|
||||
id: '85ab1864-0a92-4682-8583-82e8b696410b',
|
||||
standardId: '20202020-eda0-4cee-9577-3eb357e3c22b',
|
||||
objectMetadataId: '35763b74-1abe-4c4b-9eab-27693f0ee06d',
|
||||
type: FieldMetadataType.UUID,
|
||||
name: 'id',
|
||||
label: 'Id',
|
||||
defaultValue: 'uuid',
|
||||
description: 'Id',
|
||||
icon: 'Icon123',
|
||||
standardOverrides: null,
|
||||
options: null,
|
||||
settings: null,
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
isNullable: false,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: true,
|
||||
relationTargetFieldMetadataId: null,
|
||||
relationTargetObjectMetadataId: null,
|
||||
uniqueIdentifier: '20202020-eda0-4cee-9577-3eb357e3c22b',
|
||||
flatRelationTargetFieldMetadata: null,
|
||||
}),
|
||||
createdAt: getFlatFieldMetadataMock({
|
||||
id: 'a92b4b66-c2de-483e-8973-6f12bf36ac1a',
|
||||
standardId: '20202020-66ac-4502-9975-e4d959c50311',
|
||||
objectMetadataId: '35763b74-1abe-4c4b-9eab-27693f0ee06d',
|
||||
type: FieldMetadataType.DATE_TIME,
|
||||
name: 'createdAt',
|
||||
label: 'Creation date',
|
||||
defaultValue: 'now',
|
||||
description: 'Creation date',
|
||||
icon: 'IconCalendar',
|
||||
standardOverrides: null,
|
||||
options: null,
|
||||
settings: { displayFormat: DateDisplayFormat.RELATIVE },
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: false,
|
||||
isNullable: false,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: false,
|
||||
relationTargetFieldMetadataId: null,
|
||||
relationTargetObjectMetadataId: null,
|
||||
uniqueIdentifier: '20202020-66ac-4502-9975-e4d959c50311',
|
||||
flatRelationTargetFieldMetadata: null,
|
||||
}),
|
||||
updatedAt: getFlatFieldMetadataMock({
|
||||
id: '3907fc24-3bf0-4f0b-88be-f9635e473145',
|
||||
standardId: '20202020-d767-4622-bdcf-d8a084834d86',
|
||||
objectMetadataId: '35763b74-1abe-4c4b-9eab-27693f0ee06d',
|
||||
type: FieldMetadataType.DATE_TIME,
|
||||
name: 'updatedAt',
|
||||
label: 'Last update',
|
||||
defaultValue: 'now',
|
||||
description: 'Last time the record was changed',
|
||||
icon: 'IconCalendarClock',
|
||||
standardOverrides: null,
|
||||
options: null,
|
||||
settings: { displayFormat: DateDisplayFormat.RELATIVE },
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: false,
|
||||
isNullable: false,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: false,
|
||||
relationTargetFieldMetadataId: null,
|
||||
relationTargetObjectMetadataId: null,
|
||||
uniqueIdentifier: '20202020-d767-4622-bdcf-d8a084834d86',
|
||||
flatRelationTargetFieldMetadata: null,
|
||||
}),
|
||||
deletedAt: getFlatFieldMetadataMock({
|
||||
id: '52dcb335-5ce1-4165-a30b-4a61e84af21f',
|
||||
standardId: '20202020-b9a7-48d8-8387-b9a3090a50ec',
|
||||
objectMetadataId: '35763b74-1abe-4c4b-9eab-27693f0ee06d',
|
||||
type: FieldMetadataType.DATE_TIME,
|
||||
name: 'deletedAt',
|
||||
label: 'Deleted at',
|
||||
defaultValue: null,
|
||||
description: 'Date when the record was deleted',
|
||||
icon: 'IconCalendarMinus',
|
||||
standardOverrides: null,
|
||||
options: null,
|
||||
settings: { displayFormat: DateDisplayFormat.RELATIVE },
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: false,
|
||||
isNullable: true,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: true,
|
||||
relationTargetFieldMetadataId: null,
|
||||
relationTargetObjectMetadataId: null,
|
||||
uniqueIdentifier: '20202020-b9a7-48d8-8387-b9a3090a50ec',
|
||||
flatRelationTargetFieldMetadata: null,
|
||||
}),
|
||||
favorites: getFlatFieldMetadataMock({
|
||||
id: '71b95076-6b8d-45c9-9a7c-ab981b81bd1b',
|
||||
standardId: '20202020-b5e3-4b42-8af2-03cd4fd2e4d2',
|
||||
objectMetadataId: '35763b74-1abe-4c4b-9eab-27693f0ee06d',
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'favorites',
|
||||
label: 'Favorites',
|
||||
defaultValue: null,
|
||||
description: 'Favorites in this folder',
|
||||
icon: 'IconHeart',
|
||||
standardOverrides: null,
|
||||
options: null,
|
||||
settings: { relationType: RelationType.ONE_TO_MANY },
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
isNullable: true,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: true,
|
||||
relationTargetFieldMetadataId: 'fabbe669-b8fc-49fe-8010-f13d01eb61f1',
|
||||
relationTargetObjectMetadataId: 'f4749ffb-dde8-44ff-8b01-d3fc82df0ba2',
|
||||
uniqueIdentifier: '20202020-b5e3-4b42-8af2-03cd4fd2e4d2',
|
||||
flatRelationTargetFieldMetadata: getFlatFieldMetadataMock({
|
||||
id: 'fabbe669-b8fc-49fe-8010-f13d01eb61f1',
|
||||
standardId: '20202020-f658-4d12-8b4d-248356aa4bd9',
|
||||
objectMetadataId: 'f4749ffb-dde8-44ff-8b01-d3fc82df0ba2',
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'favoriteFolder',
|
||||
label: 'Favorite Folder',
|
||||
defaultValue: null,
|
||||
description: 'The folder this favorite belongs to',
|
||||
icon: 'IconFolder',
|
||||
standardOverrides: null,
|
||||
options: null,
|
||||
settings: {
|
||||
onDelete: RelationOnDeleteAction.SET_NULL,
|
||||
relationType: RelationType.MANY_TO_ONE,
|
||||
joinColumnName: 'favoriteFolderId',
|
||||
},
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
isNullable: true,
|
||||
isUnique: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
isLabelSyncedWithName: true,
|
||||
relationTargetFieldMetadataId: '71b95076-6b8d-45c9-9a7c-ab981b81bd1b',
|
||||
relationTargetObjectMetadataId: '35763b74-1abe-4c4b-9eab-27693f0ee06d',
|
||||
uniqueIdentifier: '20202020-f658-4d12-8b4d-248356aa4bd9',
|
||||
}),
|
||||
}),
|
||||
} as const satisfies Record<string, FlatFieldMetadata>;
|
||||
+1
-1
@@ -13,7 +13,7 @@ type FlatFieldMetadataOverrides<
|
||||
export const getFlatFieldMetadataMock = <T extends FieldMetadataType>(
|
||||
overrides: FlatFieldMetadataOverrides<T>,
|
||||
): FlatFieldMetadata => {
|
||||
const createdAt = new Date();
|
||||
const createdAt = '2024-01-01T00:00:00.000Z' as unknown as Date;
|
||||
|
||||
return {
|
||||
createdAt,
|
||||
|
||||
+13
@@ -266,6 +266,19 @@ export class FlatFieldMetadataValidatorService {
|
||||
),
|
||||
);
|
||||
} else {
|
||||
if (
|
||||
isDefined(
|
||||
parentFlatObjectMetadata.fieldsById[flatFieldMetadataToValidate.id],
|
||||
)
|
||||
) {
|
||||
errors.push(
|
||||
new ObjectMetadataException(
|
||||
'Field with same id already exists in object',
|
||||
ObjectMetadataExceptionCode.OBJECT_MUTATION_NOT_ALLOWED,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (parentFlatObjectMetadata.isRemote === true) {
|
||||
errors.push(
|
||||
new ObjectMetadataException(
|
||||
|
||||
+7
-7
@@ -8,22 +8,22 @@ import {
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
|
||||
type GetRelationFlatFieldMetadatasUtilArgs = {
|
||||
existingFlatObjectMetadataMaps: FlatObjectMetadataMaps;
|
||||
export type GetRelationFlatFieldMetadatasUtilArgs = {
|
||||
flatObjectMetadataMaps: FlatObjectMetadataMaps;
|
||||
flatFieldMetadata: FlatFieldMetadata<
|
||||
FieldMetadataType.RELATION | FieldMetadataType.MORPH_RELATION
|
||||
>;
|
||||
};
|
||||
// TODO prastoin handle morph relation
|
||||
export const getRelationFlatFieldMetadatasOrThrow = ({
|
||||
existingFlatObjectMetadataMaps,
|
||||
export const findRelationFlatFieldMetadataTargetFlatFieldMetadataOrThrow = ({
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadata,
|
||||
}: GetRelationFlatFieldMetadatasUtilArgs): FlatFieldMetadata[] => {
|
||||
}: GetRelationFlatFieldMetadatasUtilArgs): FlatFieldMetadata => {
|
||||
const { relationTargetFieldMetadataId, relationTargetObjectMetadataId } =
|
||||
flatFieldMetadata;
|
||||
|
||||
const relatedFlatObjectMetadata =
|
||||
existingFlatObjectMetadataMaps.byId[relationTargetObjectMetadataId];
|
||||
flatObjectMetadataMaps.byId[relationTargetObjectMetadataId];
|
||||
|
||||
if (!isDefined(relatedFlatObjectMetadata)) {
|
||||
throw new FieldMetadataException(
|
||||
@@ -41,5 +41,5 @@ export const getRelationFlatFieldMetadatasOrThrow = ({
|
||||
);
|
||||
}
|
||||
|
||||
return [flatFieldMetadata, relatedFlatFieldMetadata];
|
||||
return relatedFlatFieldMetadata;
|
||||
};
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import {
|
||||
findRelationFlatFieldMetadataTargetFlatFieldMetadataOrThrow,
|
||||
type GetRelationFlatFieldMetadatasUtilArgs,
|
||||
} from 'src/engine/metadata-modules/flat-field-metadata/utils/find-relation-flat-field-metadatas-target-flat-field-metadata-or-throw.util';
|
||||
|
||||
export const findRelationFlatFieldMetadataTargetFlatFieldMetadata = (
|
||||
args: GetRelationFlatFieldMetadatasUtilArgs,
|
||||
): FlatFieldMetadata | undefined => {
|
||||
try {
|
||||
return findRelationFlatFieldMetadataTargetFlatFieldMetadataOrThrow(args);
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
+8
-5
@@ -10,7 +10,7 @@ import {
|
||||
FieldMetadataExceptionCode,
|
||||
} from 'src/engine/metadata-modules/field-metadata/field-metadata.exception';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { getRelationFlatFieldMetadatasOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/get-relation-flat-field-metadatas-or-throw.util';
|
||||
import { findRelationFlatFieldMetadataTargetFlatFieldMetadataOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-relation-flat-field-metadatas-target-flat-field-metadata-or-throw.util';
|
||||
import { isFlatFieldMetadataEntityOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util';
|
||||
import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.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';
|
||||
@@ -52,10 +52,13 @@ export const fromDeleteFieldInputToFlatFieldMetadatasToDelete = ({
|
||||
FieldMetadataType.MORPH_RELATION,
|
||||
)
|
||||
) {
|
||||
return getRelationFlatFieldMetadatasOrThrow({
|
||||
existingFlatObjectMetadataMaps,
|
||||
flatFieldMetadata: flatFieldMetadataToDelete,
|
||||
});
|
||||
const relationTargetFlatFieldMetadata =
|
||||
findRelationFlatFieldMetadataTargetFlatFieldMetadataOrThrow({
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
flatFieldMetadata: flatFieldMetadataToDelete,
|
||||
});
|
||||
|
||||
return [flatFieldMetadataToDelete, relationTargetFlatFieldMetadata];
|
||||
}
|
||||
|
||||
return [flatFieldMetadataToDelete];
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ type FlatIndexFieldMetadataOverrides = Required<
|
||||
export const getFlatIndexFieldMetadataMock = (
|
||||
overrides: FlatIndexFieldMetadataOverrides,
|
||||
): FlatIndexFieldMetadata => {
|
||||
const createdAt = faker.date.anytime();
|
||||
const createdAt = '2024-01-01T00:00:00.000Z' as unknown as Date;
|
||||
|
||||
return {
|
||||
createdAt,
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { ALL_FLAT_OBJECT_METADATA_MOCKS } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/all-flat-object.mock';
|
||||
import { ALL_FLAT_OBJECT_METADATA_MOCKS } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/all-flat-object-metadatas.mock';
|
||||
import { fromFlatObjectMetadatasToFlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-flat-object-metadatas-to-flat-object-metadata-maps.util';
|
||||
|
||||
export const FLAT_OBJECT_METADATA_MAPS_MOCKS =
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`getSubFlatObjectMetadataMapsOutOfFlatFieldMetadatasOrThrow should throw when object metadata id is not found 1`] = `"getSubFlatObjectMetadataMapsOfSpecificFieldsOrThrow object metadata not found"`;
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
import { jestExpectToBeDefined } from 'test/utils/expect-to-be-defined.util.test';
|
||||
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 { PET_FLAT_FIELDS_MOCK } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/pet-flat-fields.mock';
|
||||
import { ROCKET_FLAT_FIELDS_MOCK } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/rocket-flat-fields.mock';
|
||||
import { FLAT_OBJECT_METADATA_MAPS_MOCKS } from 'src/engine/metadata-modules/flat-object-metadata-maps/mocks/flat-object-metadata-maps.mock';
|
||||
import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
import {
|
||||
type GetSubFlatObjectMetadataMapsOfSpecificFieldsOrThrowArgs,
|
||||
getSubFlatObjectMetadataMapsOutOfFlatFieldMetadatasOrThrow,
|
||||
} from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/get-sub-flat-object-metadata-maps-out-of-flat-field-metadatas-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 { fromFlatObjectMetadatasToFlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-flat-object-metadatas-to-flat-object-metadata-maps.util';
|
||||
|
||||
type GetSubObjectMetadataMapsOrThrowTestCase = {
|
||||
input: GetSubFlatObjectMetadataMapsOfSpecificFieldsOrThrowArgs;
|
||||
shouldThrow?: true;
|
||||
expected?: FlatObjectMetadataMaps;
|
||||
};
|
||||
|
||||
describe('getSubFlatObjectMetadataMapsOutOfFlatFieldMetadatasOrThrow', () => {
|
||||
const testCases: EachTestingContext<GetSubObjectMetadataMapsOrThrowTestCase>[] =
|
||||
[
|
||||
{
|
||||
title: 'should throw when object metadata id is not found',
|
||||
context: {
|
||||
input: {
|
||||
flatFieldMetadatas: [
|
||||
getFlatFieldMetadataMock({
|
||||
objectMetadataId: 'non-existent',
|
||||
type: FieldMetadataType.TEXT,
|
||||
uniqueIdentifier: 'unique-identifier-1',
|
||||
}),
|
||||
],
|
||||
flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
},
|
||||
shouldThrow: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'should extract single object metadata from maps',
|
||||
context: {
|
||||
input: {
|
||||
flatFieldMetadatas: [PET_FLAT_FIELDS_MOCK.species],
|
||||
flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
},
|
||||
expected: fromFlatObjectMetadatasToFlatObjectMetadataMaps([
|
||||
{
|
||||
...PET_FLAT_OBJECT_MOCK,
|
||||
flatFieldMetadatas: [PET_FLAT_FIELDS_MOCK.species],
|
||||
},
|
||||
]),
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'should extract object metadata from maps with provided fields',
|
||||
context: {
|
||||
input: {
|
||||
flatFieldMetadatas: [
|
||||
PET_FLAT_FIELDS_MOCK.species,
|
||||
PET_FLAT_FIELDS_MOCK.name,
|
||||
ROCKET_FLAT_FIELDS_MOCK.attachments,
|
||||
],
|
||||
flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
},
|
||||
expected: fromFlatObjectMetadatasToFlatObjectMetadataMaps([
|
||||
{
|
||||
...PET_FLAT_OBJECT_MOCK,
|
||||
flatFieldMetadatas: [
|
||||
PET_FLAT_FIELDS_MOCK.name,
|
||||
PET_FLAT_FIELDS_MOCK.species,
|
||||
],
|
||||
},
|
||||
{
|
||||
...ROCKET_FLAT_OBJECT_MOCK,
|
||||
flatFieldMetadatas: [ROCKET_FLAT_FIELDS_MOCK.attachments],
|
||||
},
|
||||
]),
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const filteredTestCases = eachTestingContextFilter(testCases);
|
||||
|
||||
it.each(filteredTestCases)(
|
||||
'$title',
|
||||
({
|
||||
context: {
|
||||
input: { flatFieldMetadatas, flatObjectMetadataMaps },
|
||||
shouldThrow = false,
|
||||
expected,
|
||||
},
|
||||
}) => {
|
||||
if (shouldThrow) {
|
||||
expect(() =>
|
||||
getSubFlatObjectMetadataMapsOutOfFlatFieldMetadatasOrThrow({
|
||||
flatFieldMetadatas,
|
||||
flatObjectMetadataMaps,
|
||||
}),
|
||||
).toThrowErrorMatchingSnapshot();
|
||||
} else {
|
||||
jestExpectToBeDefined(expected);
|
||||
const result =
|
||||
getSubFlatObjectMetadataMapsOutOfFlatFieldMetadatasOrThrow({
|
||||
flatFieldMetadatas,
|
||||
flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
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 { FLAT_OBJECT_METADATA_MAPS_MOCKS } from 'src/engine/metadata-modules/flat-object-metadata-maps/mocks/flat-object-metadata-maps.mock';
|
||||
import { type GetSubFlatObjectMetadataMapsOfSpecificFieldsOrThrowArgs } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/get-sub-flat-object-metadata-maps-out-of-flat-field-metadatas-or-throw.util';
|
||||
import { getSubFlatObjectMetadataMapsOutOfFlatFieldMetadatas } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/get-sub-flat-object-metadata-maps-out-of-flat-field-metadatas.util';
|
||||
|
||||
type GetSubFlatObjectMetadataMapsTestCase = {
|
||||
input: GetSubFlatObjectMetadataMapsOfSpecificFieldsOrThrowArgs;
|
||||
};
|
||||
|
||||
describe('getSubFlatObjectMetadataMapsOutOfFlatFieldMetadatas', () => {
|
||||
const testCases: EachTestingContext<GetSubFlatObjectMetadataMapsTestCase>[] =
|
||||
[
|
||||
{
|
||||
title: 'return undefined when object metadata id is not found',
|
||||
context: {
|
||||
input: {
|
||||
flatFieldMetadatas: [
|
||||
getFlatFieldMetadataMock({
|
||||
objectMetadataId: 'non-existent',
|
||||
type: FieldMetadataType.TEXT,
|
||||
uniqueIdentifier: 'unique-identifier-1',
|
||||
}),
|
||||
],
|
||||
flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const filteredTestCases = eachTestingContextFilter(testCases);
|
||||
|
||||
it.each(filteredTestCases)(
|
||||
'$title',
|
||||
({
|
||||
context: {
|
||||
input: { flatFieldMetadatas, flatObjectMetadataMaps },
|
||||
},
|
||||
}) => {
|
||||
const result = getSubFlatObjectMetadataMapsOutOfFlatFieldMetadatas({
|
||||
flatFieldMetadatas,
|
||||
flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
},
|
||||
);
|
||||
});
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
import { fromArrayToValuesByKeyRecord, isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { EMPTY_FLAT_OBJECT_METADATA_MAPS } from 'src/engine/metadata-modules/flat-object-metadata-maps/constant/empty-flat-object-metadata-maps.constant';
|
||||
import {
|
||||
FlatObjectMetadataMapsException,
|
||||
FlatObjectMetadataMapsExceptionCode,
|
||||
} from 'src/engine/metadata-modules/flat-object-metadata-maps/flat-object-metadata-maps.exception';
|
||||
import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
import { addFlatObjectMetadataWithFlatFieldMapsToFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/add-flat-object-metadata-with-field-maps-to-flat-object-metadata-maps-or-throw.util';
|
||||
import { deleteFieldFromFlatObjectMetadataWithFlatFieldMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/delete-field-from-flat-object-metadata-with-flat-field-maps-or-throw.util';
|
||||
|
||||
export type GetSubFlatObjectMetadataMapsOfSpecificFieldsOrThrowArgs = {
|
||||
flatFieldMetadatas: Pick<FlatFieldMetadata, 'objectMetadataId' | 'id'>[];
|
||||
flatObjectMetadataMaps: FlatObjectMetadataMaps;
|
||||
};
|
||||
export const getSubFlatObjectMetadataMapsOutOfFlatFieldMetadatasOrThrow = ({
|
||||
flatObjectMetadataMaps: sourceFlatObjectMetadataMaps,
|
||||
flatFieldMetadatas,
|
||||
}: GetSubFlatObjectMetadataMapsOfSpecificFieldsOrThrowArgs): FlatObjectMetadataMaps => {
|
||||
const fieldIdPerObjectId = fromArrayToValuesByKeyRecord({
|
||||
array: flatFieldMetadatas,
|
||||
key: 'objectMetadataId',
|
||||
});
|
||||
|
||||
return Object.keys(fieldIdPerObjectId).reduce(
|
||||
(flatObjectMetadataMaps, objectMetadataId) => {
|
||||
let flatObjectMetadataWithFlatFieldMaps = structuredClone(
|
||||
sourceFlatObjectMetadataMaps.byId[objectMetadataId],
|
||||
);
|
||||
|
||||
if (!isDefined(flatObjectMetadataWithFlatFieldMaps)) {
|
||||
throw new FlatObjectMetadataMapsException(
|
||||
'getSubFlatObjectMetadataMapsOfSpecificFieldsOrThrow object metadata not found',
|
||||
FlatObjectMetadataMapsExceptionCode.OBJECT_METADATA_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const fieldIds =
|
||||
fieldIdPerObjectId[objectMetadataId]?.map(({ id }) => id) ?? [];
|
||||
|
||||
flatObjectMetadataWithFlatFieldMaps =
|
||||
flatObjectMetadataWithFlatFieldMaps.flatFieldMetadatas.reduce(
|
||||
(flatObjectMetadataWithFlatFieldMaps, flatFieldMetadata) => {
|
||||
if (fieldIds.includes(flatFieldMetadata.id)) {
|
||||
return flatObjectMetadataWithFlatFieldMaps;
|
||||
}
|
||||
|
||||
return deleteFieldFromFlatObjectMetadataWithFlatFieldMapsOrThrow({
|
||||
fieldMetadataId: flatFieldMetadata.id,
|
||||
flatObjectMetadataWithFlatFieldMaps,
|
||||
});
|
||||
},
|
||||
flatObjectMetadataWithFlatFieldMaps,
|
||||
);
|
||||
|
||||
return addFlatObjectMetadataWithFlatFieldMapsToFlatObjectMetadataMapsOrThrow(
|
||||
{
|
||||
flatObjectMetadataWithFlatFieldMaps,
|
||||
flatObjectMetadataMaps,
|
||||
},
|
||||
);
|
||||
},
|
||||
EMPTY_FLAT_OBJECT_METADATA_MAPS,
|
||||
);
|
||||
};
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
import {
|
||||
type GetSubFlatObjectMetadataMapsOfSpecificFieldsOrThrowArgs,
|
||||
getSubFlatObjectMetadataMapsOutOfFlatFieldMetadatasOrThrow,
|
||||
} from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/get-sub-flat-object-metadata-maps-out-of-flat-field-metadatas-or-throw.util';
|
||||
|
||||
export const getSubFlatObjectMetadataMapsOutOfFlatFieldMetadatas = (
|
||||
args: GetSubFlatObjectMetadataMapsOfSpecificFieldsOrThrowArgs,
|
||||
): FlatObjectMetadataMaps | undefined => {
|
||||
try {
|
||||
return getSubFlatObjectMetadataMapsOutOfFlatFieldMetadatasOrThrow(args);
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
+10
-6
@@ -1,25 +1,29 @@
|
||||
import { ATTACHMENT_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/attachment-flat-object.mock';
|
||||
import { COMPANY_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/company-flat-object.mock';
|
||||
import { FAVORITE_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/favorite-flat-object.mock';
|
||||
import { FAVORITE_FOLDER_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/favorite-folder-flat-object.mock';
|
||||
import { NOTE_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/note-flat-object.mock';
|
||||
import { NOTETARGET_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/notetarget-flat-object.mock';
|
||||
import { NOTE_TARGET_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/note-target-flat-object.mock';
|
||||
import { OPPORTUNITY_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/opportunity-flat-object.mock';
|
||||
import { PERSON_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/person-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 { TASK_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/task-flat-object.mock';
|
||||
import { TASKTARGET_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/tasktarget-flat-object.mock';
|
||||
import { TIMELINEACTIVITY_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/timelineactivity-flat-object.mock';
|
||||
import { TASK_TARGET_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/task-target-flat-object.mock';
|
||||
import { TIMELINE_ACTIVITY_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/timeline-activity-flat-object.mock';
|
||||
|
||||
export const ALL_FLAT_OBJECT_METADATA_MOCKS = [
|
||||
TIMELINEACTIVITY_FLAT_OBJECT_MOCK,
|
||||
NOTETARGET_FLAT_OBJECT_MOCK,
|
||||
TIMELINE_ACTIVITY_FLAT_OBJECT_MOCK,
|
||||
NOTE_TARGET_FLAT_OBJECT_MOCK,
|
||||
OPPORTUNITY_FLAT_OBJECT_MOCK,
|
||||
PERSON_FLAT_OBJECT_MOCK,
|
||||
TASK_FLAT_OBJECT_MOCK,
|
||||
TASKTARGET_FLAT_OBJECT_MOCK,
|
||||
TASK_TARGET_FLAT_OBJECT_MOCK,
|
||||
PET_FLAT_OBJECT_MOCK,
|
||||
ROCKET_FLAT_OBJECT_MOCK,
|
||||
NOTE_FLAT_OBJECT_MOCK,
|
||||
COMPANY_FLAT_OBJECT_MOCK,
|
||||
ATTACHMENT_FLAT_OBJECT_MOCK,
|
||||
FAVORITE_FLAT_OBJECT_MOCK,
|
||||
FAVORITE_FOLDER_FLAT_OBJECT_MOCK,
|
||||
] as const;
|
||||
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
import { ATTACHMENT_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/attachment-flat-object.mock';
|
||||
import { COMPANY_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/company-flat-object.mock';
|
||||
import { NOTE_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/note-flat-object.mock';
|
||||
import { NOTETARGET_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/notetarget-flat-object.mock';
|
||||
import { OPPORTUNITY_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/opportunity-flat-object.mock';
|
||||
import { PERSON_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/person-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 { TASK_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/task-flat-object.mock';
|
||||
import { TASKTARGET_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/tasktarget-flat-object.mock';
|
||||
import { TIMELINEACTIVITY_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/timelineactivity-flat-object.mock';
|
||||
|
||||
export const ALL_FLAT_OBJECT_METADATA_MOCKS = [
|
||||
TIMELINEACTIVITY_FLAT_OBJECT_MOCK,
|
||||
NOTETARGET_FLAT_OBJECT_MOCK,
|
||||
OPPORTUNITY_FLAT_OBJECT_MOCK,
|
||||
PERSON_FLAT_OBJECT_MOCK,
|
||||
TASK_FLAT_OBJECT_MOCK,
|
||||
TASKTARGET_FLAT_OBJECT_MOCK,
|
||||
PET_FLAT_OBJECT_MOCK,
|
||||
ROCKET_FLAT_OBJECT_MOCK,
|
||||
NOTE_FLAT_OBJECT_MOCK,
|
||||
COMPANY_FLAT_OBJECT_MOCK,
|
||||
ATTACHMENT_FLAT_OBJECT_MOCK,
|
||||
] as const;
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
import { FAVORITE_FLAT_FIELDS_MOCK } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/favorite-flat-fields.mock';
|
||||
import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/get-flat-object-metadata.mock';
|
||||
|
||||
export const FAVORITE_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
id: 'f4749ffb-dde8-44ff-8b01-d3fc82df0ba2',
|
||||
standardId: '20202020-ab56-4e05-92a3-e2414a499860',
|
||||
nameSingular: 'favorite',
|
||||
namePlural: 'favorites',
|
||||
labelSingular: 'Favorite',
|
||||
labelPlural: 'Favorites',
|
||||
description: 'A favorite that can be accessed from the left menu',
|
||||
icon: 'IconHeart',
|
||||
standardOverrides: null,
|
||||
targetTableName: 'DEPRECATED',
|
||||
isCustom: false,
|
||||
isRemote: false,
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
isAuditLogged: true,
|
||||
isSearchable: false,
|
||||
shortcut: null,
|
||||
labelIdentifierFieldMetadataId: '49f58497-4e7c-49e6-b3bc-c91d4b8a8ddc',
|
||||
imageIdentifierFieldMetadataId: null,
|
||||
isLabelSyncedWithName: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
uniqueIdentifier: '20202020-ab56-4e05-92a3-e2414a499860',
|
||||
flatIndexMetadatas: [],
|
||||
flatFieldMetadatas: Object.values(FAVORITE_FLAT_FIELDS_MOCK),
|
||||
});
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
import { FAVORITE_FOLDER_FLAT_FIELDS_MOCK } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/favorite-folder-flat-fields.mock';
|
||||
import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/get-flat-object-metadata.mock';
|
||||
|
||||
export const FAVORITE_FOLDER_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
id: '35763b74-1abe-4c4b-9eab-27693f0ee06d',
|
||||
standardId: '20202020-7cf8-401f-8211-a9587d27fd2d',
|
||||
nameSingular: 'favoriteFolder',
|
||||
namePlural: 'favoriteFolders',
|
||||
labelSingular: 'Favorite Folder',
|
||||
labelPlural: 'Favorite Folders',
|
||||
description: 'A Folder of favorites',
|
||||
icon: 'IconFolder',
|
||||
standardOverrides: null,
|
||||
targetTableName: 'DEPRECATED',
|
||||
isCustom: false,
|
||||
isRemote: false,
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
isAuditLogged: true,
|
||||
isSearchable: false,
|
||||
shortcut: null,
|
||||
labelIdentifierFieldMetadataId: '85ab1864-0a92-4682-8583-82e8b696410b',
|
||||
imageIdentifierFieldMetadataId: null,
|
||||
isLabelSyncedWithName: false,
|
||||
workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419',
|
||||
uniqueIdentifier: '20202020-7cf8-401f-8211-a9587d27fd2d',
|
||||
flatIndexMetadatas: [],
|
||||
flatFieldMetadatas: Object.values(FAVORITE_FOLDER_FLAT_FIELDS_MOCK),
|
||||
});
|
||||
+1
-1
@@ -9,7 +9,7 @@ type FlatObjectMetadataOverrides = Required<
|
||||
export const getFlatObjectMetadataMock = (
|
||||
overrides: FlatObjectMetadataOverrides,
|
||||
): FlatObjectMetadata => {
|
||||
const createdAt = new Date();
|
||||
const createdAt = '2024-01-01T00:00:00.000Z' as unknown as Date;
|
||||
|
||||
return {
|
||||
flatFieldMetadatas: [],
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import { NOTETARGET_FLAT_FIELDS_MOCK } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/notetarget-flat-fields.mock';
|
||||
import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/get-flat-object-metadata.mock';
|
||||
|
||||
export const NOTETARGET_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
export const NOTE_TARGET_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
id: '12e3cb51-c3de-4192-b0d5-965d48d001c0',
|
||||
standardId: '20202020-fff0-4b44-be82-bda313884400',
|
||||
nameSingular: 'noteTarget',
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
import { ATTACHMENT_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/attachment-flat-object.mock';
|
||||
import { FAVORITE_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/favorite-flat-object.mock';
|
||||
import { FAVORITE_FOLDER_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/favorite-folder-flat-object.mock';
|
||||
import { NOTE_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/note-flat-object.mock';
|
||||
import { NOTE_TARGET_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/note-target-flat-object.mock';
|
||||
import { TASK_TARGET_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/task-target-flat-object.mock';
|
||||
import { TIMELINE_ACTIVITY_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/timeline-activity-flat-object.mock';
|
||||
|
||||
export const STANDARD_RELATION_TARGET_FLAT_OBJECT_METADATA_MOCKS = [
|
||||
NOTE_FLAT_OBJECT_MOCK,
|
||||
TIMELINE_ACTIVITY_FLAT_OBJECT_MOCK,
|
||||
NOTE_TARGET_FLAT_OBJECT_MOCK,
|
||||
FAVORITE_FLAT_OBJECT_MOCK,
|
||||
ATTACHMENT_FLAT_OBJECT_MOCK,
|
||||
TASK_TARGET_FLAT_OBJECT_MOCK,
|
||||
FAVORITE_FOLDER_FLAT_OBJECT_MOCK,
|
||||
] as const;
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import { TASKTARGET_FLAT_FIELDS_MOCK } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/tasktarget-flat-fields.mock';
|
||||
import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/get-flat-object-metadata.mock';
|
||||
|
||||
export const TASKTARGET_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
export const TASK_TARGET_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
id: 'bb2b29b8-7f46-4106-a8ae-3a32df9c9166',
|
||||
standardId: '20202020-5a9a-44e8-95df-771cd06d0fb1',
|
||||
nameSingular: 'taskTarget',
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import { TIMELINEACTIVITY_FLAT_FIELDS_MOCK } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/timelineactivity-flat-fields.mock';
|
||||
import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/get-flat-object-metadata.mock';
|
||||
|
||||
export const TIMELINEACTIVITY_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
export const TIMELINE_ACTIVITY_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
|
||||
id: '3090f830-c4b1-41a1-8e18-815760830bec',
|
||||
standardId: '20202020-6736-4337-b5c4-8b39fae325a5',
|
||||
nameSingular: 'timelineActivity',
|
||||
+82
-20
@@ -1,13 +1,19 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { FieldMetadataType } 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 { FailedFlatFieldMetadataValidationExceptions } from 'src/engine/metadata-modules/flat-field-metadata/types/failed-flat-field-metadata-validation.type';
|
||||
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { findRelationFlatFieldMetadataTargetFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-relation-flat-field-metadatas-target-flat-field-metadata.util';
|
||||
import { isFlatFieldMetadataEntityOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util';
|
||||
import { type 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 { addFlatObjectMetadataToFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/add-flat-object-metadata-to-flat-object-metadata-maps-or-throw.util';
|
||||
import { findFlatObjectMetadataInFlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-object-metadata-in-flat-object-metadata-maps.util';
|
||||
import { getSubFlatObjectMetadataMapsOutOfFlatFieldMetadatas } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/get-sub-flat-object-metadata-maps-out-of-flat-field-metadatas.util';
|
||||
import { FailedFlatObjectMetadataValidationExceptions } from 'src/engine/metadata-modules/flat-object-metadata/types/failed-flat-object-metadata-validation.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';
|
||||
@@ -18,13 +24,44 @@ import {
|
||||
ObjectMetadataException,
|
||||
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 { WorkspaceMigrationV2BuilderOptions } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-builder-v2.service';
|
||||
|
||||
export type ValidateOneFlatObjectMetadataArgs = {
|
||||
existingFlatObjectMetadataMaps: FlatObjectMetadataMaps;
|
||||
flatObjectMetadataToValidate: FlatObjectMetadata;
|
||||
otherFlatObjectMetadataMapsToValidate?: FlatObjectMetadataMaps;
|
||||
workspaceId: string;
|
||||
const computeRelationTargetFlatObjectMetadataMapsForValidation = ({
|
||||
flatFieldMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
}: {
|
||||
flatFieldMetadata: FlatFieldMetadata;
|
||||
flatObjectMetadataMaps: FlatObjectMetadataMaps;
|
||||
}): FlatObjectMetadataMaps | undefined => {
|
||||
if (
|
||||
!isFlatFieldMetadataEntityOfType(
|
||||
flatFieldMetadata,
|
||||
FieldMetadataType.RELATION,
|
||||
) &&
|
||||
!isFlatFieldMetadataEntityOfType(
|
||||
flatFieldMetadata,
|
||||
FieldMetadataType.MORPH_RELATION,
|
||||
)
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const relationTargetFlatFieldMetadata =
|
||||
findRelationFlatFieldMetadataTargetFlatFieldMetadata({
|
||||
flatFieldMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(relationTargetFlatFieldMetadata)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return getSubFlatObjectMetadataMapsOutOfFlatFieldMetadatas({
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadatas: [relationTargetFlatFieldMetadata],
|
||||
});
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
@@ -70,9 +107,11 @@ export class FlatObjectMetadataValidatorService {
|
||||
public validateFlatObjectMetadataDeletion({
|
||||
existingFlatObjectMetadataMaps,
|
||||
objectMetadataToDeleteId,
|
||||
buildOptions,
|
||||
}: {
|
||||
existingFlatObjectMetadataMaps: FlatObjectMetadataMaps;
|
||||
objectMetadataToDeleteId: string;
|
||||
buildOptions: WorkspaceMigrationV2BuilderOptions;
|
||||
}) {
|
||||
const errors: FailedFlatObjectMetadataValidationExceptions[] = [];
|
||||
|
||||
@@ -97,8 +136,8 @@ export class FlatObjectMetadataValidatorService {
|
||||
}
|
||||
|
||||
if (
|
||||
flatObjectMetadataToDelete.standardId !== null &&
|
||||
!flatObjectMetadataToDelete.isCustom
|
||||
!buildOptions.isSystemBuild &&
|
||||
isStandardMetadata(flatObjectMetadataToDelete)
|
||||
) {
|
||||
errors.push(
|
||||
new ObjectMetadataException(
|
||||
@@ -108,7 +147,7 @@ export class FlatObjectMetadataValidatorService {
|
||||
);
|
||||
}
|
||||
|
||||
if (flatObjectMetadataToDelete.isActive) {
|
||||
if (!buildOptions.isSystemBuild && flatObjectMetadataToDelete.isActive) {
|
||||
errors.push(
|
||||
new ObjectMetadataException(
|
||||
t`Active objects cannot be deleted`,
|
||||
@@ -124,10 +163,30 @@ export class FlatObjectMetadataValidatorService {
|
||||
public async validateFlatObjectMetadataCreation({
|
||||
existingFlatObjectMetadataMaps,
|
||||
flatObjectMetadataToValidate,
|
||||
workspaceId,
|
||||
}: ValidateOneFlatObjectMetadataArgs) {
|
||||
otherFlatObjectMetadataMapsToValidate,
|
||||
}: {
|
||||
existingFlatObjectMetadataMaps: FlatObjectMetadataMaps;
|
||||
flatObjectMetadataToValidate: FlatObjectMetadata;
|
||||
otherFlatObjectMetadataMapsToValidate?: FlatObjectMetadataMaps;
|
||||
}) {
|
||||
const errors: FailedFlatObjectMetadataValidationExceptions[] = [];
|
||||
|
||||
if (
|
||||
isDefined(
|
||||
findFlatObjectMetadataInFlatObjectMetadataMaps({
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
objectMetadataId: flatObjectMetadataToValidate.id,
|
||||
}),
|
||||
)
|
||||
) {
|
||||
errors.push(
|
||||
new ObjectMetadataException(
|
||||
t`Object with same id already exists`,
|
||||
ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (flatObjectMetadataToValidate.isRemote) {
|
||||
errors.push(
|
||||
new ObjectMetadataException(
|
||||
@@ -146,7 +205,7 @@ export class FlatObjectMetadataValidatorService {
|
||||
|
||||
const allFlatFieldMetadatasValidationErrors: FailedFlatFieldMetadataValidationExceptions[] =
|
||||
[];
|
||||
let existingFlatObjectMetadataMapsWithFlatObjectMetadataToBeCreatedWithoutFields =
|
||||
let optimisticFlatObjectMetadataMaps =
|
||||
addFlatObjectMetadataToFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadata: {
|
||||
...flatObjectMetadataToValidate,
|
||||
@@ -156,16 +215,20 @@ export class FlatObjectMetadataValidatorService {
|
||||
});
|
||||
|
||||
for (const flatFieldMetadataToValidate of flatObjectMetadataToValidate.flatFieldMetadatas) {
|
||||
const otherFlatObjectMetadataMapsToValidate = undefined; // TODO prastoin when implementing import
|
||||
|
||||
const flatFieldValidatorErrors =
|
||||
await this.flatFieldMetadataValidatorService.validateFlatFieldMetadataCreation(
|
||||
{
|
||||
existingFlatObjectMetadataMaps:
|
||||
existingFlatObjectMetadataMapsWithFlatObjectMetadataToBeCreatedWithoutFields,
|
||||
existingFlatObjectMetadataMaps: optimisticFlatObjectMetadataMaps,
|
||||
flatFieldMetadataToValidate,
|
||||
workspaceId,
|
||||
otherFlatObjectMetadataMapsToValidate,
|
||||
workspaceId: flatObjectMetadataToValidate.workspaceId,
|
||||
otherFlatObjectMetadataMapsToValidate: isDefined(
|
||||
otherFlatObjectMetadataMapsToValidate,
|
||||
)
|
||||
? computeRelationTargetFlatObjectMetadataMapsForValidation({
|
||||
flatFieldMetadata: flatFieldMetadataToValidate,
|
||||
flatObjectMetadataMaps: otherFlatObjectMetadataMapsToValidate,
|
||||
})
|
||||
: undefined,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -174,11 +237,10 @@ export class FlatObjectMetadataValidatorService {
|
||||
continue;
|
||||
}
|
||||
|
||||
existingFlatObjectMetadataMapsWithFlatObjectMetadataToBeCreatedWithoutFields =
|
||||
optimisticFlatObjectMetadataMaps =
|
||||
addFlatFieldMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatFieldMetadata: flatFieldMetadataToValidate,
|
||||
flatObjectMetadataMaps:
|
||||
existingFlatObjectMetadataMapsWithFlatObjectMetadataToBeCreatedWithoutFields,
|
||||
flatObjectMetadataMaps: optimisticFlatObjectMetadataMaps,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+8
-5
@@ -5,7 +5,7 @@ import {
|
||||
} from 'twenty-shared/utils';
|
||||
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { getRelationFlatFieldMetadatasOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/get-relation-flat-field-metadatas-or-throw.util';
|
||||
import { findRelationFlatFieldMetadataTargetFlatFieldMetadataOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-relation-flat-field-metadatas-target-flat-field-metadata-or-throw.util';
|
||||
import { isFlatFieldMetadataEntityOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util';
|
||||
import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
import { findFlatObjectMetadataInFlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-object-metadata-in-flat-object-metadata-maps.util';
|
||||
@@ -58,10 +58,13 @@ export const fromDeleteObjectInputToFlatFieldMetadatasToDelete = ({
|
||||
FieldMetadataType.MORPH_RELATION,
|
||||
)
|
||||
) {
|
||||
return getRelationFlatFieldMetadatasOrThrow({
|
||||
existingFlatObjectMetadataMaps,
|
||||
flatFieldMetadata,
|
||||
});
|
||||
const relationTargetFlatFieldMetadata =
|
||||
findRelationFlatFieldMetadataTargetFlatFieldMetadataOrThrow({
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
flatFieldMetadata,
|
||||
});
|
||||
|
||||
return [flatFieldMetadata, relationTargetFlatFieldMetadata];
|
||||
}
|
||||
|
||||
return flatFieldMetadata;
|
||||
|
||||
+3
-5
@@ -19,12 +19,12 @@ export const validateFlatObjectMetadataIdentifiers = (
|
||||
flatObjectMetadata;
|
||||
|
||||
if (isDefined(labelIdentifierFieldMetadataId)) {
|
||||
const relatedFlatFieldMetadata = flatObjectMetadata.flatFieldMetadatas.find(
|
||||
const flatFieldMetadata = flatObjectMetadata.flatFieldMetadatas.find(
|
||||
(flatFieldMetadata) =>
|
||||
flatFieldMetadata.id === labelIdentifierFieldMetadataId,
|
||||
);
|
||||
|
||||
if (!isDefined(relatedFlatFieldMetadata)) {
|
||||
if (!isDefined(flatFieldMetadata)) {
|
||||
errors.push(
|
||||
new ObjectMetadataException(
|
||||
'labelIdentifierFieldMetadataId validation failed: related field metadata not found',
|
||||
@@ -34,9 +34,7 @@ export const validateFlatObjectMetadataIdentifiers = (
|
||||
},
|
||||
),
|
||||
);
|
||||
} else if (
|
||||
!isLabelIdentifierFieldMetadataTypes(relatedFlatFieldMetadata.type)
|
||||
) {
|
||||
} else if (!isLabelIdentifierFieldMetadataTypes(flatFieldMetadata.type)) {
|
||||
errors.push(
|
||||
new ObjectMetadataException(
|
||||
'labelIdentifierFieldMetadataId validation failed: field type not compatible',
|
||||
|
||||
+76
-122
@@ -2,14 +2,13 @@ import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { MultipleMetadataValidationErrors } from 'src/engine/core-modules/error/multiple-metadata-validation-errors';
|
||||
import { EMPTY_FLAT_OBJECT_METADATA_MAPS } from 'src/engine/metadata-modules/flat-object-metadata-maps/constant/empty-flat-object-metadata-maps.constant';
|
||||
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 { deleteFieldFromFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/delete-field-from-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 { getSubFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/get-sub-flat-object-metadata-maps-or-throw.util';
|
||||
import { getSubFlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/get-sub-flat-object-metadata-maps.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 { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { fromCreateObjectInputToFlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-create-object-input-to-flat-object-metadata.util';
|
||||
import { fromDeleteObjectInputToFlatFieldMetadatasToDelete } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-delete-object-input-to-flat-field-metadatas-to-delete.util';
|
||||
@@ -24,16 +23,13 @@ import {
|
||||
ObjectMetadataExceptionCode,
|
||||
} from 'src/engine/metadata-modules/object-metadata/object-metadata.exception';
|
||||
import { WorkspaceMetadataCacheService } from 'src/engine/metadata-modules/workspace-metadata-cache/services/workspace-metadata-cache.service';
|
||||
import { WorkspaceMigrationBuilderV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-builder-v2.service';
|
||||
import { WorkspaceMigrationRunnerV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/workspace-migration-runner-v2.service';
|
||||
import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-validate-build-and-run-service';
|
||||
|
||||
@Injectable()
|
||||
export class ObjectMetadataServiceV2 {
|
||||
constructor(
|
||||
private readonly workspaceMetadataCacheService: WorkspaceMetadataCacheService,
|
||||
private readonly workspaceMigrationBuilderV2: WorkspaceMigrationBuilderV2Service,
|
||||
private readonly workspaceMigrationRunnerV2Service: WorkspaceMigrationRunnerV2Service,
|
||||
private readonly flatObjectMetadataValidatorService: FlatObjectMetadataValidatorService,
|
||||
private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService,
|
||||
) {}
|
||||
|
||||
async updateOne({
|
||||
@@ -56,43 +52,29 @@ export class ObjectMetadataServiceV2 {
|
||||
updateObjectInput,
|
||||
});
|
||||
|
||||
const validationErrors =
|
||||
this.flatObjectMetadataValidatorService.validateFlatObjectMetadataUpdate({
|
||||
existingFlatObjectMetadataMaps,
|
||||
updatedFlatObjectMetadata: optimisticallyUpdatedFlatObjectMetadata,
|
||||
const fromFlatObjectMetadataMaps = getSubFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
objectMetadataIds: [optimisticallyUpdatedFlatObjectMetadata.id],
|
||||
});
|
||||
const toFlatObjectMetadataMaps =
|
||||
replaceFlatObjectMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadata: optimisticallyUpdatedFlatObjectMetadata,
|
||||
flatObjectMetadataMaps: fromFlatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
if (validationErrors.length > 0) {
|
||||
throw new MultipleMetadataValidationErrors(
|
||||
validationErrors,
|
||||
'Multiple validation errors occurred while updating object',
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const fromFlatObjectMetadataMaps = getSubFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
objectMetadataIds: [optimisticallyUpdatedFlatObjectMetadata.id],
|
||||
});
|
||||
const toFlatObjectMetadataMaps =
|
||||
replaceFlatObjectMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadata: optimisticallyUpdatedFlatObjectMetadata,
|
||||
flatObjectMetadataMaps: fromFlatObjectMetadataMaps,
|
||||
});
|
||||
const workspaceMigration = this.workspaceMigrationBuilderV2.build({
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
||||
{
|
||||
fromFlatObjectMetadataMaps,
|
||||
toFlatObjectMetadataMaps,
|
||||
inferDeletionFromMissingObjectFieldIndex: false,
|
||||
buildOptions: {
|
||||
isSystemBuild: false,
|
||||
inferDeletionFromMissingObjectFieldIndex: false,
|
||||
},
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
await this.workspaceMigrationRunnerV2Service.run(workspaceMigration);
|
||||
} catch {
|
||||
throw new ObjectMetadataException(
|
||||
'Workspace migration failed to run',
|
||||
ObjectMetadataExceptionCode.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
errorMessage:
|
||||
'Multiple validation errors occurred while updating object',
|
||||
},
|
||||
);
|
||||
|
||||
const { flatObjectMetadataMaps: recomputedFlatObjectMetadataMaps } =
|
||||
await this.workspaceMetadataCacheService.getExistingOrRecomputeFlatObjectMetadataMaps(
|
||||
@@ -137,67 +119,51 @@ export class ObjectMetadataServiceV2 {
|
||||
});
|
||||
const { id: objectMetadataToDeleteId } = flatObjectMetadataToDelete;
|
||||
|
||||
const flatObjectDeleteValidationErrors =
|
||||
this.flatObjectMetadataValidatorService.validateFlatObjectMetadataDeletion(
|
||||
{
|
||||
existingFlatObjectMetadataMaps,
|
||||
objectMetadataToDeleteId,
|
||||
},
|
||||
);
|
||||
|
||||
if (flatObjectDeleteValidationErrors.length > 0) {
|
||||
throw new MultipleMetadataValidationErrors(
|
||||
flatObjectDeleteValidationErrors,
|
||||
'Multiple validation errors occurred while deleting object',
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const impactedObjectMetadataIds = Array.from(
|
||||
new Set(
|
||||
flatFieldMetadatasToDelete.map(
|
||||
(flatFieldMetadata) => flatFieldMetadata.objectMetadataId,
|
||||
),
|
||||
const impactedObjectMetadataIds = Array.from(
|
||||
new Set(
|
||||
flatFieldMetadatasToDelete.map(
|
||||
(flatFieldMetadata) => flatFieldMetadata.objectMetadataId,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
const fromFlatObjectMetadataMaps = getSubFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
objectMetadataIds: impactedObjectMetadataIds,
|
||||
});
|
||||
|
||||
const toFlatObjectMetadataMaps = flatFieldMetadatasToDelete
|
||||
.filter(
|
||||
(flatFieldMetadataToDelete) =>
|
||||
flatFieldMetadataToDelete.objectMetadataId !==
|
||||
objectMetadataToDeleteId,
|
||||
)
|
||||
.reduce(
|
||||
(flatObjectMetadataMaps, flatFieldMetadata) =>
|
||||
deleteFieldFromFlatObjectMetadataMapsOrThrow({
|
||||
fieldMetadataId: flatFieldMetadata.id,
|
||||
objectMetadataId: flatFieldMetadata.objectMetadataId,
|
||||
flatObjectMetadataMaps,
|
||||
}),
|
||||
deleteObjectFromFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps: fromFlatObjectMetadataMaps,
|
||||
objectMetadataId: objectMetadataToDeleteId,
|
||||
}),
|
||||
);
|
||||
|
||||
const fromFlatObjectMetadataMaps = getSubFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
objectMetadataIds: impactedObjectMetadataIds,
|
||||
});
|
||||
|
||||
const toFlatObjectMetadataMaps = flatFieldMetadatasToDelete
|
||||
.filter(
|
||||
(flatFieldMetadataToDelete) =>
|
||||
flatFieldMetadataToDelete.objectMetadataId !==
|
||||
objectMetadataToDeleteId,
|
||||
)
|
||||
.reduce(
|
||||
(flatObjectMetadataMaps, flatFieldMetadata) =>
|
||||
deleteFieldFromFlatObjectMetadataMapsOrThrow({
|
||||
fieldMetadataId: flatFieldMetadata.id,
|
||||
objectMetadataId: flatFieldMetadata.objectMetadataId,
|
||||
flatObjectMetadataMaps,
|
||||
}),
|
||||
deleteObjectFromFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps: fromFlatObjectMetadataMaps,
|
||||
objectMetadataId: objectMetadataToDeleteId,
|
||||
}),
|
||||
);
|
||||
const workspaceMigration = this.workspaceMigrationBuilderV2.build({
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
||||
{
|
||||
fromFlatObjectMetadataMaps,
|
||||
toFlatObjectMetadataMaps,
|
||||
inferDeletionFromMissingObjectFieldIndex: true,
|
||||
buildOptions: {
|
||||
inferDeletionFromMissingObjectFieldIndex: true,
|
||||
isSystemBuild: false,
|
||||
},
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
await this.workspaceMigrationRunnerV2Service.run(workspaceMigration);
|
||||
} catch {
|
||||
throw new ObjectMetadataException(
|
||||
'Workspace migration failed to run',
|
||||
ObjectMetadataExceptionCode.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
errorMessage:
|
||||
'Multiple validation errors occurred while deleting object',
|
||||
},
|
||||
);
|
||||
|
||||
return fromFlatObjectMetadataToObjectMetadataDto(
|
||||
flatObjectMetadataToDelete,
|
||||
@@ -224,41 +190,29 @@ export class ObjectMetadataServiceV2 {
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
const validationErrors =
|
||||
await this.flatObjectMetadataValidatorService.validateFlatObjectMetadataCreation(
|
||||
{
|
||||
existingFlatObjectMetadataMaps,
|
||||
flatObjectMetadataToValidate: flatObjectMetadataToCreate,
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
const fromFlatObjectMetadataMaps =
|
||||
getSubFlatObjectMetadataMaps({
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
objectMetadataIds: [flatObjectMetadataToCreate.id],
|
||||
}) ?? EMPTY_FLAT_OBJECT_METADATA_MAPS;
|
||||
|
||||
if (validationErrors.length > 0) {
|
||||
throw new MultipleMetadataValidationErrors(
|
||||
validationErrors,
|
||||
'Multiple validation errors occurred while creating object',
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const workspaceMigration = this.workspaceMigrationBuilderV2.build({
|
||||
fromFlatObjectMetadataMaps: EMPTY_FLAT_OBJECT_METADATA_MAPS,
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
||||
{
|
||||
fromFlatObjectMetadataMaps,
|
||||
toFlatObjectMetadataMaps:
|
||||
addFlatObjectMetadataToFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps: EMPTY_FLAT_OBJECT_METADATA_MAPS,
|
||||
flatObjectMetadata: flatObjectMetadataToCreate,
|
||||
}),
|
||||
inferDeletionFromMissingObjectFieldIndex: false,
|
||||
buildOptions: {
|
||||
isSystemBuild: false,
|
||||
inferDeletionFromMissingObjectFieldIndex: false,
|
||||
},
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
await this.workspaceMigrationRunnerV2Service.run(workspaceMigration);
|
||||
} catch {
|
||||
throw new ObjectMetadataException(
|
||||
'Workspace migration failed to run',
|
||||
ObjectMetadataExceptionCode.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
errorMessage:
|
||||
'Multiple validation errors occurred while creating object',
|
||||
},
|
||||
);
|
||||
|
||||
const { flatObjectMetadataMaps: recomputedFlatObjectMetadataMaps } =
|
||||
await this.workspaceMetadataCacheService.getExistingOrRecomputeFlatObjectMetadataMaps(
|
||||
|
||||
+2
-4
@@ -43,8 +43,7 @@ import { WorkspacePermissionsCacheModule } from 'src/engine/metadata-modules/wor
|
||||
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
|
||||
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
|
||||
import { WorkspaceMigrationRunnerModule } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.module';
|
||||
import { WorkspaceMigrationBuilderV2Module } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-builder-v2.module';
|
||||
import { WorkspaceMigrationRunnerV2Module } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/workspace-migration-runner-v2.module';
|
||||
import { WorkspaceMigrationV2Module } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-v2.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -69,8 +68,7 @@ import { WorkspaceMigrationRunnerV2Module } from 'src/engine/workspace-manager/w
|
||||
WorkspaceMetadataCacheModule,
|
||||
WorkspaceDataSourceModule,
|
||||
FeatureFlagModule,
|
||||
WorkspaceMigrationBuilderV2Module,
|
||||
WorkspaceMigrationRunnerV2Module,
|
||||
WorkspaceMigrationV2Module,
|
||||
],
|
||||
services: [
|
||||
ObjectMetadataService,
|
||||
|
||||
+1408
-86
File diff suppressed because it is too large
Load Diff
+176
-106
@@ -1,5 +1,4 @@
|
||||
import { getFlatIndexMetadataMock } from 'src/engine/metadata-modules/flat-index-metadata/__mocks__/get-flat-index-metadata.mock';
|
||||
import { EMPTY_FLAT_OBJECT_METADATA_MAPS } from 'src/engine/metadata-modules/flat-object-metadata-maps/constant/empty-flat-object-metadata-maps.constant';
|
||||
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';
|
||||
@@ -8,116 +7,187 @@ import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-obje
|
||||
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,
|
||||
},
|
||||
),
|
||||
},
|
||||
expectedActionsTypeCounter: {
|
||||
deleteObject: 1,
|
||||
deleteField: ROCKET_FLAT_OBJECT_MOCK.flatFieldMetadatas.length,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title:
|
||||
'It should build a delete_object action with standard activated object',
|
||||
context: {
|
||||
input: {
|
||||
buildOptions: {
|
||||
inferDeletionFromMissingObjectFieldIndex: true,
|
||||
isSystemBuild: true,
|
||||
},
|
||||
fromFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
toFlatObjectMetadataMaps: deleteObjectFromFlatObjectMetadataMapsOrThrow(
|
||||
{
|
||||
flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
objectMetadataId: COMPANY_FLAT_OBJECT_MOCK.id,
|
||||
},
|
||||
),
|
||||
},
|
||||
expectedActionsTypeCounter: {
|
||||
deleteObject: 1,
|
||||
deleteField: COMPANY_FLAT_OBJECT_MOCK.flatFieldMetadatas.length,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
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,
|
||||
]),
|
||||
},
|
||||
expectedActionsTypeCounter: {
|
||||
createObject: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
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,
|
||||
]),
|
||||
},
|
||||
expectedActionsTypeCounter: {
|
||||
createObject: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title:
|
||||
'It should build a create_object and create_index actions for each of this fieldMetadata',
|
||||
context: {
|
||||
expectedActionsTypeCounter: {
|
||||
createIndex: 1,
|
||||
createObject: 1,
|
||||
},
|
||||
input: {
|
||||
fromFlatObjectMetadataMaps:
|
||||
fromFlatObjectMetadatasToFlatObjectMetadataMaps([
|
||||
...STANDARD_RELATION_TARGET_FLAT_OBJECT_METADATA_MOCKS,
|
||||
]),
|
||||
toFlatObjectMetadataMaps:
|
||||
fromFlatObjectMetadatasToFlatObjectMetadataMaps([
|
||||
...STANDARD_RELATION_TARGET_FLAT_OBJECT_METADATA_MOCKS,
|
||||
{
|
||||
...ROCKET_FLAT_OBJECT_MOCK,
|
||||
flatIndexMetadatas: [
|
||||
getFlatIndexMetadataMock({
|
||||
objectMetadataId: ROCKET_FLAT_OBJECT_MOCK.id,
|
||||
uniqueIdentifier: 'field-metadata-unique-identifier-1',
|
||||
}),
|
||||
],
|
||||
},
|
||||
]),
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
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,
|
||||
}),
|
||||
}),
|
||||
},
|
||||
expectedActionsTypeCounter: {
|
||||
updateObject: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
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',
|
||||
},
|
||||
}),
|
||||
}),
|
||||
},
|
||||
expectedActionsTypeCounter: {
|
||||
updateObject: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const WORKSPACE_MIGRATION_OBJECT_BUILDER_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,
|
||||
}),
|
||||
}),
|
||||
},
|
||||
expectedActionsTypeCounter: {
|
||||
updateObject: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
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',
|
||||
},
|
||||
}),
|
||||
}),
|
||||
},
|
||||
expectedActionsTypeCounter: {
|
||||
updateObject: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'It should build a create_object action',
|
||||
context: {
|
||||
input: {
|
||||
fromFlatObjectMetadataMaps:
|
||||
fromFlatObjectMetadatasToFlatObjectMetadataMaps([
|
||||
PET_FLAT_OBJECT_MOCK,
|
||||
]),
|
||||
toFlatObjectMetadataMaps:
|
||||
fromFlatObjectMetadatasToFlatObjectMetadataMaps([
|
||||
PET_FLAT_OBJECT_MOCK,
|
||||
ROCKET_FLAT_OBJECT_MOCK,
|
||||
]),
|
||||
},
|
||||
expectedActionsTypeCounter: {
|
||||
createObject: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title:
|
||||
'It should build a create_object and create_index actions for each of this fieldMetadata',
|
||||
context: {
|
||||
input: {
|
||||
fromFlatObjectMetadataMaps: EMPTY_FLAT_OBJECT_METADATA_MAPS,
|
||||
toFlatObjectMetadataMaps:
|
||||
fromFlatObjectMetadatasToFlatObjectMetadataMaps([
|
||||
{
|
||||
...ROCKET_FLAT_OBJECT_MOCK,
|
||||
flatIndexMetadatas: [
|
||||
getFlatIndexMetadataMock({
|
||||
objectMetadataId: ROCKET_FLAT_OBJECT_MOCK.id,
|
||||
uniqueIdentifier: 'field-metadata-unique-identifier-1',
|
||||
}),
|
||||
],
|
||||
},
|
||||
]),
|
||||
},
|
||||
|
||||
expectedActionsTypeCounter: {
|
||||
createObject: 1,
|
||||
createField: 0,
|
||||
createIndex: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'It should build a delete_object action',
|
||||
context: {
|
||||
input: {
|
||||
fromFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
toFlatObjectMetadataMaps:
|
||||
deleteObjectFromFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
objectMetadataId: COMPANY_FLAT_OBJECT_MOCK.id,
|
||||
}),
|
||||
},
|
||||
expectedActionsTypeCounter: {
|
||||
deleteObject: 1,
|
||||
deleteField: COMPANY_FLAT_OBJECT_MOCK.flatFieldMetadatas.length,
|
||||
},
|
||||
},
|
||||
},
|
||||
...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: {
|
||||
|
||||
+8
-2
@@ -11,9 +11,15 @@ export type ExpectedActionCounters = Partial<
|
||||
Record<CamelCasedWorkspaceMigrationActionsType, number>
|
||||
>;
|
||||
|
||||
type TestWorkspaceMigrationBuildArgs = Omit<
|
||||
WorkspaceMigrationBuildArgs,
|
||||
'workspaceId' | 'buildOptions'
|
||||
> &
|
||||
Partial<Pick<WorkspaceMigrationBuildArgs, 'buildOptions'>>;
|
||||
|
||||
export type WorkspaceMigrationBuilderTestCase = EachTestingContext<{
|
||||
input:
|
||||
| Omit<WorkspaceMigrationBuildArgs, 'workspaceId'>
|
||||
| (() => Omit<WorkspaceMigrationBuildArgs, 'workspaceId'>);
|
||||
| TestWorkspaceMigrationBuildArgs
|
||||
| (() => TestWorkspaceMigrationBuildArgs);
|
||||
expectedActionsTypeCounter?: ExpectedActionCounters;
|
||||
}>;
|
||||
|
||||
+44
-7
@@ -2,6 +2,11 @@ import { extractRecordIdsAndDatesAsExpectAny } from 'test/utils/extract-record-i
|
||||
import { eachTestingContextFilter } from 'twenty-shared/testing';
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
|
||||
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 { WorkspaceMigrationV2ObjectActionsBuilder } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-v2-object-actions-builder';
|
||||
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_INDEX_BUILDER_TEST_CASES } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/common/workspace-migration-builder-index-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';
|
||||
@@ -74,31 +79,63 @@ 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: WorkspaceMigrationV2ObjectActionsBuilder;
|
||||
|
||||
beforeEach(() => {
|
||||
service = new WorkspaceMigrationBuilderV2Service();
|
||||
featureFlagService = {
|
||||
isFeatureEnabled: jest.fn().mockResolvedValue(true),
|
||||
} as any;
|
||||
|
||||
flatFieldMetadataTypeValidatorService =
|
||||
new FlatFieldMetadataTypeValidatorService(featureFlagService);
|
||||
|
||||
flatFieldMetadataValidatorService = new FlatFieldMetadataValidatorService(
|
||||
flatFieldMetadataTypeValidatorService,
|
||||
);
|
||||
|
||||
flatObjectMetadataValidatorService =
|
||||
new FlatObjectMetadataValidatorService(
|
||||
flatFieldMetadataValidatorService,
|
||||
);
|
||||
|
||||
objectActionsBuilder = new WorkspaceMigrationV2ObjectActionsBuilder(
|
||||
flatObjectMetadataValidatorService,
|
||||
);
|
||||
service = new WorkspaceMigrationBuilderV2Service(objectActionsBuilder);
|
||||
});
|
||||
|
||||
it.each(eachTestingContextFilter(testCases))(
|
||||
'$title',
|
||||
({ context: { input, expectedActionsTypeCounter } }) => {
|
||||
async ({ context: { input, expectedActionsTypeCounter } }) => {
|
||||
const {
|
||||
fromFlatObjectMetadataMaps,
|
||||
toFlatObjectMetadataMaps,
|
||||
inferDeletionFromMissingObjectFieldIndex,
|
||||
buildOptions = {
|
||||
inferDeletionFromMissingObjectFieldIndex: true,
|
||||
isSystemBuild: false,
|
||||
},
|
||||
} = typeof input === 'function' ? input() : input;
|
||||
const workspaceMigration = service.build({
|
||||
const validateAndBuildResult = await service.validateAndBuild({
|
||||
buildOptions,
|
||||
fromFlatObjectMetadataMaps,
|
||||
toFlatObjectMetadataMaps,
|
||||
inferDeletionFromMissingObjectFieldIndex,
|
||||
workspaceId: '20202020-52cc-4c64-ad63-76c26fc3a1e1',
|
||||
});
|
||||
|
||||
expect(validateAndBuildResult.status).toBe('success');
|
||||
if (validateAndBuildResult.status !== 'success') {
|
||||
throw new Error('Should never occur');
|
||||
}
|
||||
|
||||
expectedActionsTypeCounterChecker({
|
||||
expectedActionsTypeCounter,
|
||||
workspaceMigration,
|
||||
workspaceMigration: validateAndBuildResult.workspaceMigration,
|
||||
});
|
||||
const { actions } = workspaceMigration;
|
||||
const { actions } = validateAndBuildResult.workspaceMigration;
|
||||
|
||||
expect(actions).toMatchSnapshot(
|
||||
actions.map(extractRecordIdsAndDatesAsExpectAny),
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import { type FailedAndSuccessfulMetadataValidateAndBuildRecord } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/failed-and-successful-metadata-validate-and-build-record.type';
|
||||
|
||||
export const EMPTY_FAILED_AND_SUCCESSFUL_METADATA_BUILD_RECORD = {
|
||||
failed: [],
|
||||
successful: [],
|
||||
} as const satisfies FailedAndSuccessfulMetadataValidateAndBuildRecord;
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import { type FailedMetadataValidateAndBuild } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/failed-metadata-validate-and-build.type';
|
||||
import { type SuccessfulMetadataValidateAndBuild } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/successful-metadata-validate-and-build.type';
|
||||
import { type WorkspaceMigrationActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-action-common-v2';
|
||||
|
||||
export type FailedAndSuccessfulMetadataValidateAndBuildRecord<
|
||||
T extends WorkspaceMigrationActionV2 = WorkspaceMigrationActionV2,
|
||||
> = {
|
||||
failed: FailedMetadataValidateAndBuild[];
|
||||
successful: SuccessfulMetadataValidateAndBuild<T>[];
|
||||
};
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import { type FailedFlatFieldMetadataValidationExceptions } from 'src/engine/metadata-modules/flat-field-metadata/types/failed-flat-field-metadata-validation.type';
|
||||
import { type FailedFlatObjectMetadataValidationExceptions } from 'src/engine/metadata-modules/flat-object-metadata/types/failed-flat-object-metadata-validation.type';
|
||||
|
||||
export type FailedMetadataValidateAndBuild = {
|
||||
status: 'fail';
|
||||
errors: (
|
||||
| FailedFlatFieldMetadataValidationExceptions
|
||||
| FailedFlatObjectMetadataValidationExceptions
|
||||
)[];
|
||||
};
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
import { type FailedAndSuccessfulMetadataValidateAndBuildRecord } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/failed-and-successful-metadata-validate-and-build-record.type';
|
||||
import { type WorkspaceMigrationActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-action-common-v2';
|
||||
|
||||
export type MetadataActionValidateAndBuildResult<
|
||||
T extends WorkspaceMigrationActionV2,
|
||||
> = {
|
||||
results: FailedAndSuccessfulMetadataValidateAndBuildRecord<T>;
|
||||
optimisticFlatObjectMetadataMaps: FlatObjectMetadataMaps;
|
||||
};
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
import { type FailedMetadataValidateAndBuild } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/failed-metadata-validate-and-build.type';
|
||||
import { type SuccessfulMetadataValidateAndBuild } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/successful-metadata-validate-and-build.type';
|
||||
import { type WorkspaceMigrationActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-action-common-v2';
|
||||
|
||||
export type MetadataValidateAndBuildResult<
|
||||
T extends WorkspaceMigrationActionV2 = WorkspaceMigrationActionV2,
|
||||
> = FailedMetadataValidateAndBuild | SuccessfulMetadataValidateAndBuild<T>;
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import { type WorkspaceMigrationActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-action-common-v2';
|
||||
|
||||
export type SuccessfulMetadataValidateAndBuild<
|
||||
T extends WorkspaceMigrationActionV2 = WorkspaceMigrationActionV2,
|
||||
> = {
|
||||
status: 'success';
|
||||
result: T;
|
||||
};
|
||||
+4
-4
@@ -9,7 +9,7 @@ import {
|
||||
|
||||
export type UpdatedObjectMetadataDeletedCreatedUpdatedFieldMatrix = {
|
||||
flatObjectMetadata: FlatObjectMetadata;
|
||||
} & CustomDeletedCreatedUpdatedMatrix<'fieldMetadata', FlatFieldMetadata>;
|
||||
} & CustomDeletedCreatedUpdatedMatrix<'flatFieldMetadata', FlatFieldMetadata>;
|
||||
|
||||
export const computeUpdatedObjectMetadataDeletedCreatedUpdatedFieldMatrix = (
|
||||
updatedObjectMetadata: FromTo<FlatObjectMetadata>[],
|
||||
@@ -25,9 +25,9 @@ export const computeUpdatedObjectMetadataDeletedCreatedUpdatedFieldMatrix = (
|
||||
|
||||
matrixAccumulator.push({
|
||||
flatObjectMetadata: to,
|
||||
createdFieldMetadata: fieldMetadataMatrix.created,
|
||||
deletedFieldMetadata: fieldMetadataMatrix.deleted,
|
||||
updatedFieldMetadata: fieldMetadataMatrix.updated,
|
||||
createdFlatFieldMetadata: fieldMetadataMatrix.created,
|
||||
deletedFlatFieldMetadata: fieldMetadataMatrix.deleted,
|
||||
updatedFlatFieldMetadata: fieldMetadataMatrix.updated,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import { EMPTY_FAILED_AND_SUCCESSFUL_METADATA_BUILD_RECORD } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/constants/empty-failed-and-successful-metadata-build-record.constant';
|
||||
import { type FailedAndSuccessfulMetadataValidateAndBuildRecord } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/failed-and-successful-metadata-validate-and-build-record.type';
|
||||
import { type MetadataValidateAndBuildResult } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/metadata-validate-and-build-result.type';
|
||||
import { type WorkspaceMigrationActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-action-common-v2';
|
||||
|
||||
export const fromMetadataBuildResultsToFailedAndSuccessfulMetadataBuildRecord =
|
||||
<T extends WorkspaceMigrationActionV2>(
|
||||
metadataBuildResults: MetadataValidateAndBuildResult<T>[],
|
||||
): FailedAndSuccessfulMetadataValidateAndBuildRecord<T> =>
|
||||
metadataBuildResults.reduce(
|
||||
(acc, buildResult) =>
|
||||
buildResult.status === 'fail'
|
||||
? {
|
||||
...acc,
|
||||
failed: [...acc.failed, buildResult],
|
||||
}
|
||||
: { ...acc, successful: [...acc.successful, buildResult] },
|
||||
EMPTY_FAILED_AND_SUCCESSFUL_METADATA_BUILD_RECORD,
|
||||
);
|
||||
+11
-1
@@ -1,11 +1,21 @@
|
||||
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 { WorkspaceMigrationBuilderV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-builder-v2.service';
|
||||
import { WorkspaceMigrationV2ObjectActionsBuilder } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-v2-object-actions-builder';
|
||||
|
||||
@Module({
|
||||
imports: [FeatureFlagModule],
|
||||
providers: [WorkspaceMigrationBuilderV2Service],
|
||||
providers: [
|
||||
WorkspaceMigrationBuilderV2Service,
|
||||
FlatFieldMetadataValidatorService,
|
||||
FlatFieldMetadataTypeValidatorService,
|
||||
WorkspaceMigrationV2ObjectActionsBuilder,
|
||||
FlatObjectMetadataValidatorService,
|
||||
],
|
||||
exports: [WorkspaceMigrationBuilderV2Service],
|
||||
})
|
||||
export class WorkspaceMigrationBuilderV2Module {}
|
||||
|
||||
+79
-38
@@ -5,28 +5,44 @@ import { type FromTo } from 'twenty-shared/types';
|
||||
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 { type WorkspaceMigrationV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-v2';
|
||||
import { FailedMetadataValidateAndBuild } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/failed-metadata-validate-and-build.type';
|
||||
import { WorkspaceMigrationV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-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';
|
||||
import { computeUpdatedObjectMetadataDeletedCreatedUpdatedIndexMatrix } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/compute-updated-object-metadata-deleted-created-updated-index-matrix.util';
|
||||
import { getWorkspaceMigrationV2FieldDeleteAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/get-workspace-migration-v2-field-actions';
|
||||
import { getWorkspaceMigrationV2CreateIndexAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/get-workspace-migration-v2-index-actions';
|
||||
import { buildWorkspaceMigrationV2FieldActions } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-v2-field-actions-builder';
|
||||
import { buildWorkspaceMigrationIndexActions } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-v2-index-actions-builder';
|
||||
import { buildWorkspaceMigrationV2ObjectActions } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-v2-object-actions-builder';
|
||||
import { WorkspaceMigrationV2ObjectActionsBuilder } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-v2-object-actions-builder';
|
||||
|
||||
export type WorkspaceMigrationV2BuilderOptions = {
|
||||
inferDeletionFromMissingObjectFieldIndex: boolean;
|
||||
isSystemBuild: boolean;
|
||||
};
|
||||
|
||||
export type WorkspaceMigrationBuildArgs = {
|
||||
workspaceId: string;
|
||||
inferDeletionFromMissingObjectFieldIndex?: boolean;
|
||||
buildOptions: WorkspaceMigrationV2BuilderOptions;
|
||||
// Could from be optional ? we still have a race condition when if we recompute cache it could have been different than when we calculated the to
|
||||
} & FromTo<FlatObjectMetadataMaps, 'FlatObjectMetadataMaps'>;
|
||||
@Injectable()
|
||||
export class WorkspaceMigrationBuilderV2Service {
|
||||
constructor() {}
|
||||
constructor(
|
||||
private readonly workspaceMigrationV2ObjectActionsBuilder: WorkspaceMigrationV2ObjectActionsBuilder,
|
||||
) {}
|
||||
|
||||
build({
|
||||
public async validateAndBuild({
|
||||
fromFlatObjectMetadataMaps,
|
||||
toFlatObjectMetadataMaps,
|
||||
workspaceId,
|
||||
inferDeletionFromMissingObjectFieldIndex = true,
|
||||
}: WorkspaceMigrationBuildArgs): WorkspaceMigrationV2 {
|
||||
buildOptions,
|
||||
}: WorkspaceMigrationBuildArgs): Promise<
|
||||
| FailedMetadataValidateAndBuild
|
||||
| {
|
||||
status: 'success';
|
||||
workspaceMigration: WorkspaceMigrationV2;
|
||||
}
|
||||
> {
|
||||
const fromFlatObjectMetadatas =
|
||||
fromFlatObjectMetadataMapsToFlatObjectMetadatas(
|
||||
fromFlatObjectMetadataMaps,
|
||||
@@ -34,33 +50,50 @@ export class WorkspaceMigrationBuilderV2Service {
|
||||
const toFlatObjectMetadatas =
|
||||
fromFlatObjectMetadataMapsToFlatObjectMetadatas(toFlatObjectMetadataMaps);
|
||||
|
||||
// Dispatch
|
||||
const {
|
||||
created: createdObjectMetadata,
|
||||
deleted: deletedObjectMetadata,
|
||||
updated: updatedObjectMetadata,
|
||||
created: createdFlatObjectMetadata,
|
||||
deleted: deletedFlatObjectMetadata,
|
||||
updated: updatedFlatObjectMetadata,
|
||||
} = deletedCreatedUpdatedMatrixDispatcher({
|
||||
from: fromFlatObjectMetadatas,
|
||||
to: toFlatObjectMetadatas,
|
||||
});
|
||||
|
||||
const objectMetadataDeletedCreatedUpdatedIndex =
|
||||
computeUpdatedObjectMetadataDeletedCreatedUpdatedIndexMatrix(
|
||||
updatedFlatObjectMetadata,
|
||||
);
|
||||
|
||||
const objectMetadataDeletedCreatedUpdatedFields =
|
||||
computeUpdatedObjectMetadataDeletedCreatedUpdatedFieldMatrix(
|
||||
updatedFlatObjectMetadata,
|
||||
);
|
||||
///
|
||||
|
||||
// Validate and build
|
||||
const objectWorkspaceMigrationActions =
|
||||
buildWorkspaceMigrationV2ObjectActions({
|
||||
createdObjectMetadata,
|
||||
deletedObjectMetadata,
|
||||
updatedObjectMetadata,
|
||||
inferDeletionFromMissingObjectFieldIndex,
|
||||
});
|
||||
await this.workspaceMigrationV2ObjectActionsBuilder.validateAndBuildObjectActions(
|
||||
{
|
||||
fromFlatObjectMetadataMaps,
|
||||
createdFlatObjectMetadata,
|
||||
deletedFlatObjectMetadata,
|
||||
toFlatObjectMetadataMaps,
|
||||
updatedFlatObjectMetadata,
|
||||
buildOptions,
|
||||
},
|
||||
);
|
||||
|
||||
const createdObjectMetadataCreateIndexActions =
|
||||
createdObjectMetadata.flatMap((objectMetadata) =>
|
||||
createdFlatObjectMetadata.flatMap((objectMetadata) =>
|
||||
objectMetadata.flatIndexMetadatas.map(
|
||||
getWorkspaceMigrationV2CreateIndexAction,
|
||||
),
|
||||
);
|
||||
|
||||
const deletedObjectWorkspaceMigrationDeleteFieldActions =
|
||||
inferDeletionFromMissingObjectFieldIndex
|
||||
? deletedObjectMetadata.flatMap((flatObjectMetadata) =>
|
||||
buildOptions.inferDeletionFromMissingObjectFieldIndex
|
||||
? deletedFlatObjectMetadata.flatMap((flatObjectMetadata) =>
|
||||
flatObjectMetadata.flatFieldMetadatas.map((flatFieldMetadata) =>
|
||||
getWorkspaceMigrationV2FieldDeleteAction({
|
||||
flatFieldMetadata,
|
||||
@@ -70,35 +103,43 @@ export class WorkspaceMigrationBuilderV2Service {
|
||||
)
|
||||
: [];
|
||||
|
||||
const objectMetadataDeletedCreatedUpdatedFields =
|
||||
computeUpdatedObjectMetadataDeletedCreatedUpdatedFieldMatrix(
|
||||
updatedObjectMetadata,
|
||||
);
|
||||
|
||||
const fieldWorkspaceMigrationActions =
|
||||
buildWorkspaceMigrationV2FieldActions({
|
||||
inferDeletionFromMissingObjectFieldIndex,
|
||||
inferDeletionFromMissingObjectFieldIndex:
|
||||
buildOptions.inferDeletionFromMissingObjectFieldIndex,
|
||||
objectMetadataDeletedCreatedUpdatedFields,
|
||||
});
|
||||
|
||||
const objectMetadataDeletedCreatedUpdatedIndex =
|
||||
computeUpdatedObjectMetadataDeletedCreatedUpdatedIndexMatrix(
|
||||
updatedObjectMetadata,
|
||||
);
|
||||
const indexWorkspaceMigrationActions = buildWorkspaceMigrationIndexActions({
|
||||
objectMetadataDeletedCreatedUpdatedIndex,
|
||||
inferDeletionFromMissingObjectFieldIndex,
|
||||
inferDeletionFromMissingObjectFieldIndex:
|
||||
buildOptions.inferDeletionFromMissingObjectFieldIndex,
|
||||
});
|
||||
///
|
||||
|
||||
if (objectWorkspaceMigrationActions.results.failed.length > 0) {
|
||||
return {
|
||||
status: 'fail',
|
||||
errors: objectWorkspaceMigrationActions.results.failed.flatMap(
|
||||
(el) => el.errors,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
workspaceId,
|
||||
actions: [
|
||||
...deletedObjectWorkspaceMigrationDeleteFieldActions,
|
||||
...objectWorkspaceMigrationActions,
|
||||
...createdObjectMetadataCreateIndexActions,
|
||||
...fieldWorkspaceMigrationActions,
|
||||
...indexWorkspaceMigrationActions,
|
||||
],
|
||||
status: 'success',
|
||||
workspaceMigration: {
|
||||
workspaceId,
|
||||
actions: [
|
||||
...deletedObjectWorkspaceMigrationDeleteFieldActions,
|
||||
...objectWorkspaceMigrationActions.results.successful.map(
|
||||
(el) => el.result,
|
||||
), // TMP create util
|
||||
...createdObjectMetadataCreateIndexActions,
|
||||
...fieldWorkspaceMigrationActions,
|
||||
...indexWorkspaceMigrationActions,
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+13
-13
@@ -21,13 +21,13 @@ export const buildWorkspaceMigrationV2FieldActions = ({
|
||||
[];
|
||||
|
||||
for (const {
|
||||
createdFieldMetadata,
|
||||
deletedFieldMetadata,
|
||||
updatedFieldMetadata,
|
||||
createdFlatFieldMetadata,
|
||||
deletedFlatFieldMetadata,
|
||||
updatedFlatFieldMetadata,
|
||||
flatObjectMetadata,
|
||||
} of objectMetadataDeletedCreatedUpdatedFields) {
|
||||
const updateFieldActions = updatedFieldMetadata.flatMap<UpdateFieldAction>(
|
||||
({ from, to }) => {
|
||||
const updateFieldActions =
|
||||
updatedFlatFieldMetadata.flatMap<UpdateFieldAction>(({ from, to }) => {
|
||||
const updates = compareTwoFlatFieldMetadata({
|
||||
from,
|
||||
to,
|
||||
@@ -44,18 +44,18 @@ export const buildWorkspaceMigrationV2FieldActions = ({
|
||||
workspaceId: to.workspaceId,
|
||||
updates,
|
||||
};
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
const createFieldAction = createdFieldMetadata.map((flatFieldMetadata) =>
|
||||
getWorkspaceMigrationV2FieldCreateAction({
|
||||
flatFieldMetadata,
|
||||
flatObjectMetadata,
|
||||
}),
|
||||
const createFieldAction = createdFlatFieldMetadata.map(
|
||||
(flatFieldMetadata) =>
|
||||
getWorkspaceMigrationV2FieldCreateAction({
|
||||
flatFieldMetadata,
|
||||
flatObjectMetadata,
|
||||
}),
|
||||
);
|
||||
|
||||
const deleteFieldAction = inferDeletionFromMissingObjectFieldIndex
|
||||
? deletedFieldMetadata.map((flatFieldMetadata) =>
|
||||
? deletedFlatFieldMetadata.map((flatFieldMetadata) =>
|
||||
getWorkspaceMigrationV2FieldDeleteAction({
|
||||
flatFieldMetadata,
|
||||
flatObjectMetadata,
|
||||
|
||||
+175
-41
@@ -1,68 +1,202 @@
|
||||
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 { 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 { MetadataActionValidateAndBuildResult } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/metadata-action-validate-and-build-result.type';
|
||||
import { MetadataValidateAndBuildResult } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/metadata-validate-and-build-result.type';
|
||||
import {
|
||||
type UpdateObjectAction,
|
||||
type WorkspaceMigrationObjectActionV2,
|
||||
UpdateObjectAction,
|
||||
WorkspaceMigrationObjectActionV2,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-object-action-v2';
|
||||
import { fromMetadataBuildResultsToFailedAndSuccessfulMetadataBuildRecord } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/utils/from-metadata-build-results-to-failed-and-successful-metadata-build-record.util';
|
||||
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 { WorkspaceMigrationV2BuilderOptions } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-builder-v2.service';
|
||||
|
||||
export type CreatedDeletedUpdatedObjectMetadataInputMatrix =
|
||||
CustomDeletedCreatedUpdatedMatrix<'objectMetadata', FlatObjectMetadata> & {
|
||||
inferDeletionFromMissingObjectFieldIndex: boolean;
|
||||
export type CreatedDeletedUpdatedObjectMetadataInputMatrix = FromTo<
|
||||
FlatObjectMetadataMaps,
|
||||
'FlatObjectMetadataMaps'
|
||||
> &
|
||||
CustomDeletedCreatedUpdatedMatrix<
|
||||
'flatObjectMetadata',
|
||||
FlatObjectMetadata
|
||||
> & {
|
||||
buildOptions: WorkspaceMigrationV2BuilderOptions;
|
||||
fromFlatObjectMetadataMaps: FlatObjectMetadataMaps; // should be renamed to from
|
||||
};
|
||||
export const buildWorkspaceMigrationV2ObjectActions = ({
|
||||
createdObjectMetadata,
|
||||
deletedObjectMetadata,
|
||||
updatedObjectMetadata,
|
||||
inferDeletionFromMissingObjectFieldIndex,
|
||||
}: CreatedDeletedUpdatedObjectMetadataInputMatrix): WorkspaceMigrationObjectActionV2[] => {
|
||||
const createdObjectActions = createdObjectMetadata.map(
|
||||
(flatObjectMetadata) => {
|
||||
const createFieldActions = flatObjectMetadata.flatFieldMetadatas.map(
|
||||
(flatFieldMetadata) =>
|
||||
getWorkspaceMigrationV2FieldCreateAction({
|
||||
flatFieldMetadata,
|
||||
flatObjectMetadata,
|
||||
}),
|
||||
);
|
||||
|
||||
return getWorkspaceMigrationV2ObjectCreateAction({
|
||||
flatObjectMetadata,
|
||||
createFieldActions,
|
||||
});
|
||||
},
|
||||
);
|
||||
@Injectable()
|
||||
export class WorkspaceMigrationV2ObjectActionsBuilder {
|
||||
constructor(
|
||||
private readonly flatObjectMetadataValidatorService: FlatObjectMetadataValidatorService,
|
||||
) {}
|
||||
|
||||
const deletedObjectActions = inferDeletionFromMissingObjectFieldIndex
|
||||
? deletedObjectMetadata.map(getWorkspaceMigrationV2ObjectDeleteAction)
|
||||
: [];
|
||||
public async validateAndBuildObjectActions({
|
||||
createdFlatObjectMetadata,
|
||||
deletedFlatObjectMetadata,
|
||||
updatedFlatObjectMetadata,
|
||||
buildOptions,
|
||||
fromFlatObjectMetadataMaps,
|
||||
toFlatObjectMetadataMaps,
|
||||
}: CreatedDeletedUpdatedObjectMetadataInputMatrix): Promise<
|
||||
MetadataActionValidateAndBuildResult<WorkspaceMigrationObjectActionV2>
|
||||
> {
|
||||
let optimisticFlatObjectMetadataMaps = structuredClone(
|
||||
fromFlatObjectMetadataMaps,
|
||||
);
|
||||
const createdObjectBuildResults = await Promise.all(
|
||||
createdFlatObjectMetadata.map<
|
||||
Promise<
|
||||
MetadataValidateAndBuildResult<WorkspaceMigrationObjectActionV2>
|
||||
>
|
||||
>(async (flatObjectMetadata) => {
|
||||
const validationErrors =
|
||||
await this.flatObjectMetadataValidatorService.validateFlatObjectMetadataCreation(
|
||||
{
|
||||
existingFlatObjectMetadataMaps: optimisticFlatObjectMetadataMaps,
|
||||
flatObjectMetadataToValidate: flatObjectMetadata,
|
||||
otherFlatObjectMetadataMapsToValidate: toFlatObjectMetadataMaps,
|
||||
},
|
||||
);
|
||||
|
||||
const updatedObjectActions =
|
||||
updatedObjectMetadata.flatMap<UpdateObjectAction>(({ from, to }) => {
|
||||
if (validationErrors.length > 0) {
|
||||
return {
|
||||
status: 'fail',
|
||||
errors: validationErrors,
|
||||
};
|
||||
}
|
||||
|
||||
optimisticFlatObjectMetadataMaps =
|
||||
addFlatObjectMetadataToFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadata: flatObjectMetadata,
|
||||
flatObjectMetadataMaps: optimisticFlatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
const createFieldActions = flatObjectMetadata.flatFieldMetadatas.map(
|
||||
(flatFieldMetadata) =>
|
||||
getWorkspaceMigrationV2FieldCreateAction({
|
||||
flatFieldMetadata,
|
||||
flatObjectMetadata,
|
||||
}),
|
||||
);
|
||||
|
||||
const createObjectAction = getWorkspaceMigrationV2ObjectCreateAction({
|
||||
flatObjectMetadata,
|
||||
createFieldActions,
|
||||
});
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
result: createObjectAction,
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
const deletedObjectBuildResults =
|
||||
buildOptions.inferDeletionFromMissingObjectFieldIndex
|
||||
? deletedFlatObjectMetadata.map<
|
||||
MetadataValidateAndBuildResult<WorkspaceMigrationObjectActionV2>
|
||||
>((flatObjectMetadataToDelete) => {
|
||||
const validationErrors =
|
||||
this.flatObjectMetadataValidatorService.validateFlatObjectMetadataDeletion(
|
||||
{
|
||||
buildOptions,
|
||||
existingFlatObjectMetadataMaps:
|
||||
optimisticFlatObjectMetadataMaps,
|
||||
objectMetadataToDeleteId: flatObjectMetadataToDelete.id,
|
||||
},
|
||||
);
|
||||
|
||||
if (validationErrors.length > 0) {
|
||||
return {
|
||||
status: 'fail',
|
||||
errors: validationErrors,
|
||||
};
|
||||
}
|
||||
|
||||
optimisticFlatObjectMetadataMaps =
|
||||
deleteObjectFromFlatObjectMetadataMapsOrThrow({
|
||||
objectMetadataId: flatObjectMetadataToDelete.id,
|
||||
flatObjectMetadataMaps: optimisticFlatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
const deleteObjectAction =
|
||||
getWorkspaceMigrationV2ObjectDeleteAction(
|
||||
flatObjectMetadataToDelete,
|
||||
);
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
result: deleteObjectAction,
|
||||
};
|
||||
})
|
||||
: [];
|
||||
|
||||
const updatedObjectBuildResults = updatedFlatObjectMetadata.flatMap<
|
||||
MetadataValidateAndBuildResult<WorkspaceMigrationObjectActionV2>
|
||||
>(({ from: fromFlatObjectMetadata, to: toFlatObjectMetadata }) => {
|
||||
const objectUpdatedProperties = compareTwoFlatObjectMetadata({
|
||||
from,
|
||||
to,
|
||||
from: fromFlatObjectMetadata,
|
||||
to: toFlatObjectMetadata,
|
||||
});
|
||||
|
||||
if (objectUpdatedProperties.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return {
|
||||
const validationErrors =
|
||||
this.flatObjectMetadataValidatorService.validateFlatObjectMetadataUpdate(
|
||||
{
|
||||
existingFlatObjectMetadataMaps: optimisticFlatObjectMetadataMaps,
|
||||
updatedFlatObjectMetadata: toFlatObjectMetadata,
|
||||
},
|
||||
);
|
||||
|
||||
if (validationErrors.length > 0) {
|
||||
return {
|
||||
status: 'fail',
|
||||
errors: validationErrors,
|
||||
};
|
||||
}
|
||||
|
||||
optimisticFlatObjectMetadataMaps =
|
||||
replaceFlatObjectMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadata: toFlatObjectMetadata,
|
||||
flatObjectMetadataMaps: optimisticFlatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
const updateObjectAction: UpdateObjectAction = {
|
||||
type: 'update_object',
|
||||
objectMetadataId: to.id,
|
||||
objectMetadataId: toFlatObjectMetadata.id,
|
||||
updates: objectUpdatedProperties,
|
||||
};
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
result: updateObjectAction,
|
||||
};
|
||||
});
|
||||
|
||||
return [
|
||||
...createdObjectActions,
|
||||
...deletedObjectActions,
|
||||
...updatedObjectActions,
|
||||
];
|
||||
};
|
||||
return {
|
||||
optimisticFlatObjectMetadataMaps,
|
||||
results: fromMetadataBuildResultsToFailedAndSuccessfulMetadataBuildRecord(
|
||||
[
|
||||
...createdObjectBuildResults,
|
||||
...deletedObjectBuildResults,
|
||||
...updatedObjectBuildResults,
|
||||
],
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -46,6 +46,8 @@ import { WorkspaceSchemaObjectActionRunnerService } from 'src/engine/workspace-m
|
||||
WorkspaceSchemaObjectActionRunnerService,
|
||||
WorkspaceSchemaIndexActionRunnerService,
|
||||
WorkspaceSchemaFieldActionRunnerService,
|
||||
WorkspaceMetadataMigrationRunnerService,
|
||||
WorkspaceSchemaMigrationRunnerService,
|
||||
],
|
||||
})
|
||||
export class WorkspaceMigrationRunnerV2Module {}
|
||||
|
||||
+7
-2
@@ -2,13 +2,18 @@ import { Module } from '@nestjs/common';
|
||||
|
||||
import { WorkspaceMigrationBuilderV2Module } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-builder-v2.module';
|
||||
import { WorkspaceMigrationRunnerV2Module } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/workspace-migration-runner-v2.module';
|
||||
import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-validate-build-and-run-service';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
WorkspaceMigrationBuilderV2Module,
|
||||
WorkspaceMigrationRunnerV2Module,
|
||||
],
|
||||
providers: [],
|
||||
exports: [WorkspaceMigrationRunnerV2Module],
|
||||
providers: [WorkspaceMigrationValidateBuildAndRunService],
|
||||
exports: [
|
||||
WorkspaceMigrationRunnerV2Module,
|
||||
WorkspaceMigrationBuilderV2Module,
|
||||
WorkspaceMigrationValidateBuildAndRunService,
|
||||
],
|
||||
})
|
||||
export class WorkspaceMigrationV2Module {}
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { MultipleMetadataValidationErrors } from 'src/engine/core-modules/error/multiple-metadata-validation-errors';
|
||||
import {
|
||||
WorkspaceMigrationBuildArgs,
|
||||
WorkspaceMigrationBuilderV2Service,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-builder-v2.service';
|
||||
import { WorkspaceMigrationRunnerV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/workspace-migration-runner-v2.service';
|
||||
import {
|
||||
WorkspaceMigrationV2Exception,
|
||||
WorkspaceMigrationV2ExceptionCode,
|
||||
} from 'src/engine/workspace-manager/workspace-migration.exception';
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceMigrationValidateBuildAndRunService {
|
||||
constructor(
|
||||
private readonly workspaceMigrationBuilderV2Service: WorkspaceMigrationBuilderV2Service,
|
||||
private readonly workspaceMigrationRunnerV2Service: WorkspaceMigrationRunnerV2Service,
|
||||
) {}
|
||||
|
||||
public async validateBuildAndRunWorkspaceMigration({
|
||||
errorMessage,
|
||||
...builderArgs
|
||||
}: WorkspaceMigrationBuildArgs & { errorMessage: string }) {
|
||||
const validateAndBuildResult = await this.workspaceMigrationBuilderV2Service
|
||||
.validateAndBuild(builderArgs)
|
||||
.catch((error) => {
|
||||
throw new WorkspaceMigrationV2Exception(
|
||||
WorkspaceMigrationV2ExceptionCode.BUILDER_INTERNAL_SERVER_ERROR,
|
||||
error.message,
|
||||
);
|
||||
});
|
||||
|
||||
if (validateAndBuildResult.status === 'fail') {
|
||||
throw new MultipleMetadataValidationErrors(
|
||||
validateAndBuildResult.errors,
|
||||
errorMessage,
|
||||
);
|
||||
}
|
||||
|
||||
await this.workspaceMigrationRunnerV2Service
|
||||
.run(validateAndBuildResult.workspaceMigration)
|
||||
.catch((error) => {
|
||||
throw new WorkspaceMigrationV2Exception(
|
||||
WorkspaceMigrationV2ExceptionCode.RUNNER_INTERNAL_SERVER_ERROR,
|
||||
error.message,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { CustomException } from 'src/utils/custom-exception';
|
||||
|
||||
export class WorkspaceMigrationV2Exception extends CustomException<WorkspaceMigrationV2ExceptionCode> {}
|
||||
|
||||
export enum WorkspaceMigrationV2ExceptionCode {
|
||||
BUILDER_INTERNAL_SERVER_ERROR = 'BUILDER_INTERNAL_SERVER_ERROR',
|
||||
RUNNER_INTERNAL_SERVER_ERROR = 'RUNNER_INTERNAL_SERVER_ERROR',
|
||||
}
|
||||
@@ -18,13 +18,6 @@ export const extractRecordIdsAndDatesAsExpectAny = (
|
||||
return acc;
|
||||
}
|
||||
|
||||
if (key.endsWith('Id') || key === 'id') {
|
||||
return {
|
||||
...acc,
|
||||
[key]: expect.any(String),
|
||||
};
|
||||
}
|
||||
|
||||
if (value instanceof Date) {
|
||||
return {
|
||||
...acc,
|
||||
@@ -32,6 +25,19 @@ export const extractRecordIdsAndDatesAsExpectAny = (
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
key.endsWith('Id') ||
|
||||
key === 'id' ||
|
||||
key === 'updatedAt' ||
|
||||
key === 'deletedAt' ||
|
||||
key === 'createdAt'
|
||||
) {
|
||||
return {
|
||||
...acc,
|
||||
[key]: expect.any(String),
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof value === 'object' || Array.isArray(value)) {
|
||||
return {
|
||||
...acc,
|
||||
|
||||
Reference in New Issue
Block a user