FieldMetadataServiceV2.deleteOneField (#13744)
# Introduction - Migrated the `deleteOneField` handler to new workspace migration v2 style - Refactored the build to expect `flatObjectMetadataMaps` that he instantly translate at the be beginning Commented `ObjectMetadataServiceV2` as it's still not implemented neither used
This commit is contained in:
+25
-3
@@ -11,6 +11,8 @@ import {
|
||||
import { type FieldMetadataType } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
||||
import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter';
|
||||
import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe';
|
||||
import {
|
||||
@@ -28,8 +30,8 @@ import { DeleteOneFieldInput } from 'src/engine/metadata-modules/field-metadata/
|
||||
import { FieldMetadataDTO } from 'src/engine/metadata-modules/field-metadata/dtos/field-metadata.dto';
|
||||
import { RelationDTO } from 'src/engine/metadata-modules/field-metadata/dtos/relation.dto';
|
||||
import {
|
||||
type UpdateFieldInput,
|
||||
UpdateOneFieldMetadataInput,
|
||||
type UpdateFieldInput,
|
||||
} from 'src/engine/metadata-modules/field-metadata/dtos/update-field.input';
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import {
|
||||
@@ -38,6 +40,7 @@ import {
|
||||
} from 'src/engine/metadata-modules/field-metadata/field-metadata.exception';
|
||||
import { BeforeUpdateOneField } from 'src/engine/metadata-modules/field-metadata/hooks/before-update-one-field.hook';
|
||||
import { FieldMetadataService } from 'src/engine/metadata-modules/field-metadata/services/field-metadata.service';
|
||||
import { FieldMetadataServiceV2 } from 'src/engine/metadata-modules/field-metadata/services/field-metadata.service-v2';
|
||||
import { fieldMetadataGraphqlApiExceptionHandler } from 'src/engine/metadata-modules/field-metadata/utils/field-metadata-graphql-api-exception-handler.util';
|
||||
import { fromFieldMetadataEntityToFieldMetadataDto } from 'src/engine/metadata-modules/field-metadata/utils/from-field-metadata-entity-to-field-metadata-dto.util';
|
||||
import { fromObjectMetadataEntityToObjectMetadataDto } from 'src/engine/metadata-modules/field-metadata/utils/from-object-metadata-entity-to-object-metadata-dto.util';
|
||||
@@ -57,6 +60,8 @@ export class FieldMetadataResolver {
|
||||
constructor(
|
||||
private readonly fieldMetadataService: FieldMetadataService,
|
||||
private readonly beforeUpdateOneField: BeforeUpdateOneField<UpdateFieldInput>,
|
||||
private readonly featureFlagService: FeatureFlagService,
|
||||
private readonly fieldMetadataServiceV2: FieldMetadataServiceV2,
|
||||
) {}
|
||||
|
||||
@UseGuards(SettingsPermissionsGuard(PermissionFlagType.DATA_MODEL))
|
||||
@@ -103,10 +108,27 @@ export class FieldMetadataResolver {
|
||||
@Args('input') input: DeleteOneFieldInput,
|
||||
@AuthWorkspace() { id: workspaceId }: Workspace,
|
||||
) {
|
||||
if (!workspaceId) {
|
||||
if (!isDefined(workspaceId)) {
|
||||
throw new ForbiddenError('Could not retrieve workspace ID');
|
||||
}
|
||||
|
||||
try {
|
||||
const isWorkspaceMigrationV2Enabled =
|
||||
await this.featureFlagService.isFeatureEnabled(
|
||||
FeatureFlagKey.IS_WORKSPACE_MIGRATION_V2_ENABLED,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
if (isWorkspaceMigrationV2Enabled) {
|
||||
return await this.fieldMetadataServiceV2.deleteOneField({
|
||||
deleteOneFieldInput: input,
|
||||
workspaceId,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
fieldMetadataGraphqlApiExceptionHandler(error);
|
||||
}
|
||||
|
||||
const fieldMetadata =
|
||||
await this.fieldMetadataService.findOneWithinWorkspace(workspaceId, {
|
||||
where: {
|
||||
@@ -114,7 +136,7 @@ export class FieldMetadataResolver {
|
||||
},
|
||||
});
|
||||
|
||||
if (!fieldMetadata) {
|
||||
if (!isDefined(fieldMetadata)) {
|
||||
throw new ValidationError('Field does not exist');
|
||||
}
|
||||
|
||||
|
||||
+118
-31
@@ -1,13 +1,16 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { Repository } from 'typeorm';
|
||||
import {
|
||||
isDefined,
|
||||
trimAndRemoveDuplicatedWhitespacesFromString,
|
||||
} from 'twenty-shared/utils';
|
||||
import { In, Repository } from 'typeorm';
|
||||
|
||||
import { MultipleMetadataValidationErrors } from 'src/engine/core-modules/error/multiple-metadata-validation-errors';
|
||||
import { type CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input';
|
||||
import { type DeleteOneFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/delete-field.input';
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import {
|
||||
FieldMetadataException,
|
||||
@@ -16,20 +19,21 @@ import {
|
||||
import { FlatFieldMetadataValidatorService } from 'src/engine/metadata-modules/flat-field-metadata/services/flat-field-metadata-validator.service';
|
||||
import { type FailedFlatFieldMetadataValidationExceptions } from 'src/engine/metadata-modules/flat-field-metadata/types/failed-flat-field-metadata-validation.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { fromCreateFieldInputToFlatFieldAndItsFlatObjectMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/from-create-field-input-to-flat-field-and-its-flat-object-metadata.util';
|
||||
import { fromCreateFieldInputToFlatFieldMetadatasToCreate } from 'src/engine/metadata-modules/flat-field-metadata/utils/from-create-field-input-to-flat-field-metadatas-to-create.util';
|
||||
import { fromDeleteFieldInputToFlatFieldMetadatasToDelete } from 'src/engine/metadata-modules/flat-field-metadata/utils/from-delete-field-input-to-flat-field-metadatas-to-delete.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 { addFlatFieldMetadataInFlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/add-flat-field-metadata-in-flat-object-metadata-maps.util';
|
||||
import { deleteFieldFromFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/delete-field-from-flat-object-metadata-maps-or-throw.util';
|
||||
import { extractFlatObjectMetadataMapsOutOfFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/extract-flat-object-metadata-maps-out-of-flat-object-metadata-maps-or-throw.util';
|
||||
import { extractFlatObjectMetadataMapsOutOfFlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/extract-flat-object-metadata-maps-out-of-flat-object-metadata-maps.util';
|
||||
import { fromFlatObjectMetadataMapsToFlatObjectMetadatas } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-flat-object-metadata-maps-to-flat-object-metadatas.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';
|
||||
|
||||
@Injectable()
|
||||
export class FieldMetadataServiceV2 extends TypeOrmQueryService<FieldMetadataEntity> {
|
||||
export class FieldMetadataServiceV2 {
|
||||
constructor(
|
||||
@InjectRepository(FieldMetadataEntity, 'core')
|
||||
private readonly fieldMetadataRepository: Repository<FieldMetadataEntity>,
|
||||
@@ -37,14 +41,19 @@ export class FieldMetadataServiceV2 extends TypeOrmQueryService<FieldMetadataEnt
|
||||
private readonly workspaceMigrationBuilderV2: WorkspaceMigrationBuilderV2Service,
|
||||
private readonly flatFieldMetadataValidatorService: FlatFieldMetadataValidatorService,
|
||||
private readonly workspaceMigrationRunnerV2Service: WorkspaceMigrationRunnerV2Service,
|
||||
) {
|
||||
super(fieldMetadataRepository);
|
||||
}
|
||||
) {}
|
||||
|
||||
override async createOne(
|
||||
fieldMetadataInput: CreateFieldInput,
|
||||
): Promise<FieldMetadataEntity> {
|
||||
const [createdFieldMetadata] = await this.createMany([fieldMetadataInput]);
|
||||
async createOne({
|
||||
fieldMetadataInput,
|
||||
workspaceId,
|
||||
}: {
|
||||
fieldMetadataInput: Omit<CreateFieldInput, 'workspaceId'>;
|
||||
workspaceId: string;
|
||||
}): Promise<FieldMetadataEntity> {
|
||||
const [createdFieldMetadata] = await this.createMany({
|
||||
fieldMetadataInputs: [fieldMetadataInput],
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
if (!isDefined(createdFieldMetadata)) {
|
||||
throw new FieldMetadataException(
|
||||
@@ -56,6 +65,76 @@ export class FieldMetadataServiceV2 extends TypeOrmQueryService<FieldMetadataEnt
|
||||
return createdFieldMetadata;
|
||||
}
|
||||
|
||||
public async deleteOneField({
|
||||
deleteOneFieldInput,
|
||||
workspaceId,
|
||||
}: {
|
||||
deleteOneFieldInput: DeleteOneFieldInput;
|
||||
workspaceId: string;
|
||||
}): Promise<void> {
|
||||
const { flatObjectMetadataMaps: existingFlatObjectMetadataMaps } =
|
||||
await this.workspaceMetadataCacheService.getExistingOrRecomputeFlatObjectMetadataMaps(
|
||||
{
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
const flatFieldMetadatasToDelete =
|
||||
fromDeleteFieldInputToFlatFieldMetadatasToDelete({
|
||||
deleteOneFieldInput,
|
||||
existingFlatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
const validationErrors = flatFieldMetadatasToDelete.flatMap(
|
||||
(flatFieldMetadataToDelete) =>
|
||||
this.flatFieldMetadataValidatorService.validateFlatFieldMetadataDeletion(
|
||||
{
|
||||
existingFlatObjectMetadataMaps,
|
||||
flatFieldMetadataToDelete,
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
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',
|
||||
);
|
||||
}
|
||||
|
||||
const flatObjectMetadataMapsWithImpactedObject =
|
||||
extractFlatObjectMetadataMapsOutOfFlatObjectMetadataMapsOrThrow({
|
||||
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,
|
||||
});
|
||||
|
||||
await this.workspaceMigrationRunnerV2Service.run(workspaceMigration);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
private computeOtherFlatObjectMetadataMapsToValidate({
|
||||
flatFieldMetadataToCreate,
|
||||
flatFieldMetadatasToCreate,
|
||||
@@ -107,15 +186,17 @@ export class FieldMetadataServiceV2 extends TypeOrmQueryService<FieldMetadataEnt
|
||||
});
|
||||
}
|
||||
|
||||
async createMany(
|
||||
fieldMetadataInputs: CreateFieldInput[],
|
||||
): Promise<FieldMetadataEntity[]> {
|
||||
async createMany({
|
||||
fieldMetadataInputs,
|
||||
workspaceId,
|
||||
}: {
|
||||
fieldMetadataInputs: Omit<CreateFieldInput, 'workspaceId'>[];
|
||||
workspaceId: string;
|
||||
}): Promise<FieldMetadataEntity[]> {
|
||||
if (!fieldMetadataInputs.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const workspaceId = fieldMetadataInputs[0].workspaceId;
|
||||
|
||||
const { flatObjectMetadataMaps: existingFlatObjectMetadataMaps } =
|
||||
await this.workspaceMetadataCacheService.getExistingOrRecomputeFlatObjectMetadataMaps(
|
||||
{ workspaceId },
|
||||
@@ -125,8 +206,9 @@ export class FieldMetadataServiceV2 extends TypeOrmQueryService<FieldMetadataEnt
|
||||
await Promise.all(
|
||||
fieldMetadataInputs.map(
|
||||
async (fieldMetadataInput) =>
|
||||
await fromCreateFieldInputToFlatFieldAndItsFlatObjectMetadata({
|
||||
await fromCreateFieldInputToFlatFieldMetadatasToCreate({
|
||||
existingFlatObjectMetadataMaps,
|
||||
workspaceId,
|
||||
rawCreateFieldInput: fieldMetadataInput,
|
||||
}),
|
||||
),
|
||||
@@ -192,33 +274,38 @@ export class FieldMetadataServiceV2 extends TypeOrmQueryService<FieldMetadataEnt
|
||||
);
|
||||
|
||||
try {
|
||||
const fromImpactedFlatObjectMetadataMaps =
|
||||
const fromFlatObjectMetadataMaps =
|
||||
extractFlatObjectMetadataMapsOutOfFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
objectMetadataIds: impactedObjectMetadataIds,
|
||||
});
|
||||
const toImpactedFlatObjectMetadataMaps =
|
||||
const toFlatObjectMetadataMaps =
|
||||
extractFlatObjectMetadataMapsOutOfFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps: optimisticFlatObjectMetadataMaps,
|
||||
objectMetadataIds: impactedObjectMetadataIds,
|
||||
});
|
||||
const workspaceMigration = this.workspaceMigrationBuilderV2.build({
|
||||
objectMetadataFromToInputs: {
|
||||
from: fromFlatObjectMetadataMapsToFlatObjectMetadatas(
|
||||
fromImpactedFlatObjectMetadataMaps,
|
||||
),
|
||||
to: fromFlatObjectMetadataMapsToFlatObjectMetadatas(
|
||||
toImpactedFlatObjectMetadataMaps,
|
||||
),
|
||||
},
|
||||
fromFlatObjectMetadataMaps,
|
||||
toFlatObjectMetadataMaps,
|
||||
inferDeletionFromMissingObjectFieldIndex: false,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
await this.workspaceMigrationRunnerV2Service.run(workspaceMigration);
|
||||
|
||||
// TODO refactor once the runner has been refactored to return created entities
|
||||
return [];
|
||||
// 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(
|
||||
|
||||
+11
-5
@@ -7,10 +7,10 @@ import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
DataSource,
|
||||
type FindOneOptions,
|
||||
In,
|
||||
type QueryRunner,
|
||||
Repository,
|
||||
type FindOneOptions,
|
||||
type QueryRunner,
|
||||
} from 'typeorm';
|
||||
|
||||
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
|
||||
@@ -58,9 +58,9 @@ import { WorkspaceMetadataVersionService } from 'src/engine/metadata-modules/wor
|
||||
import { generateMigrationName } from 'src/engine/metadata-modules/workspace-migration/utils/generate-migration-name.util';
|
||||
import {
|
||||
WorkspaceMigrationColumnActionType,
|
||||
WorkspaceMigrationTableActionType,
|
||||
type WorkspaceMigrationColumnDrop,
|
||||
type WorkspaceMigrationTableAction,
|
||||
WorkspaceMigrationTableActionType,
|
||||
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
|
||||
import { WorkspaceMigrationFactory } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.factory';
|
||||
import { WorkspaceMigrationService } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.service';
|
||||
@@ -111,7 +111,10 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
||||
);
|
||||
|
||||
if (isWorkspaceMigrationV2Enabled) {
|
||||
return this.fieldMetadataServiceV2.createOne(fieldMetadataInput);
|
||||
return this.fieldMetadataServiceV2.createOne({
|
||||
fieldMetadataInput,
|
||||
workspaceId: fieldMetadataInput.workspaceId,
|
||||
});
|
||||
}
|
||||
|
||||
const [createdFieldMetadata] = await this.createMany([fieldMetadataInput]);
|
||||
@@ -589,7 +592,10 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
||||
);
|
||||
|
||||
if (isWorkspaceMigrationV2Enabled) {
|
||||
return this.fieldMetadataServiceV2.createMany(fieldMetadataInputs);
|
||||
return this.fieldMetadataServiceV2.createMany({
|
||||
fieldMetadataInputs,
|
||||
workspaceId,
|
||||
});
|
||||
}
|
||||
|
||||
const { objectMetadataMaps } =
|
||||
|
||||
+60
@@ -1,5 +1,6 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { type FieldMetadataType } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
@@ -38,6 +39,65 @@ export class FlatFieldMetadataValidatorService {
|
||||
private readonly flatFieldMetadataTypeValidatorService: FlatFieldMetadataTypeValidatorService,
|
||||
) {}
|
||||
|
||||
validateFlatFieldMetadataDeletion({
|
||||
existingFlatObjectMetadataMaps,
|
||||
flatFieldMetadataToDelete,
|
||||
}: {
|
||||
flatFieldMetadataToDelete: FlatFieldMetadata;
|
||||
existingFlatObjectMetadataMaps: FlatObjectMetadataMaps;
|
||||
}): FailedFlatFieldMetadataValidationExceptions[] {
|
||||
const errors: FailedFlatFieldMetadataValidationExceptions[] = [];
|
||||
|
||||
const flatObjectMetadataWithFieldMaps =
|
||||
existingFlatObjectMetadataMaps.byId[
|
||||
flatFieldMetadataToDelete.objectMetadataId
|
||||
];
|
||||
|
||||
if (!isDefined(flatObjectMetadataWithFieldMaps)) {
|
||||
errors.push(
|
||||
new FieldMetadataException(
|
||||
'field to delete object metadata not found',
|
||||
FieldMetadataExceptionCode.OBJECT_METADATA_NOT_FOUND,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
if (
|
||||
flatObjectMetadataWithFieldMaps.labelIdentifierFieldMetadataId ===
|
||||
flatFieldMetadataToDelete.id
|
||||
) {
|
||||
errors.push(
|
||||
new FieldMetadataException(
|
||||
'Cannot delete, please update the label identifier field first',
|
||||
FieldMetadataExceptionCode.FIELD_MUTATION_NOT_ALLOWED,
|
||||
{
|
||||
userFriendlyMessage: t`Cannot delete, please update the label identifier field first`,
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!flatFieldMetadataToDelete.isCustom) {
|
||||
errors.push(
|
||||
new FieldMetadataException(
|
||||
"Standard Fields can't be deleted",
|
||||
FieldMetadataExceptionCode.INVALID_FIELD_INPUT,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (flatFieldMetadataToDelete.isActive) {
|
||||
errors.push(
|
||||
new FieldMetadataException(
|
||||
"Active fields can't be deleted",
|
||||
FieldMetadataExceptionCode.INVALID_FIELD_INPUT,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
async validateOneFlatFieldMetadata<
|
||||
T extends FieldMetadataType = FieldMetadataType,
|
||||
>({
|
||||
|
||||
+7
-2
@@ -21,12 +21,14 @@ import { getDefaultFlatFieldMetadata } from 'src/engine/metadata-modules/flat-fi
|
||||
import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
|
||||
type FromCreateFieldInputToFlatObjectMetadata = {
|
||||
rawCreateFieldInput: CreateFieldInput;
|
||||
rawCreateFieldInput: Omit<CreateFieldInput, 'workspaceId'>;
|
||||
existingFlatObjectMetadataMaps: FlatObjectMetadataMaps;
|
||||
workspaceId: string;
|
||||
};
|
||||
|
||||
export const fromCreateFieldInputToFlatFieldAndItsFlatObjectMetadata = async ({
|
||||
export const fromCreateFieldInputToFlatFieldMetadatasToCreate = async ({
|
||||
rawCreateFieldInput,
|
||||
workspaceId,
|
||||
existingFlatObjectMetadataMaps,
|
||||
}: FromCreateFieldInputToFlatObjectMetadata): Promise<FlatFieldMetadata[]> => {
|
||||
if (rawCreateFieldInput.isRemoteCreation) {
|
||||
@@ -57,11 +59,13 @@ export const fromCreateFieldInputToFlatFieldAndItsFlatObjectMetadata = async ({
|
||||
const fieldMetadataId = v4();
|
||||
const commonFlatFieldMetadata = getDefaultFlatFieldMetadata({
|
||||
createFieldInput,
|
||||
workspaceId,
|
||||
fieldMetadataId,
|
||||
});
|
||||
|
||||
switch (createFieldInput.type) {
|
||||
case FieldMetadataType.MORPH_RELATION: {
|
||||
// TODO prastoin
|
||||
throw new UserInputError(
|
||||
'Morph relation feature is not migrated to workspace migration v2 yet',
|
||||
);
|
||||
@@ -71,6 +75,7 @@ export const fromCreateFieldInputToFlatFieldAndItsFlatObjectMetadata = async ({
|
||||
existingFlatObjectMetadataMaps,
|
||||
sourceParentFlatObjectMetadata: parentFlatObjectMetadata,
|
||||
createFieldInput,
|
||||
workspaceId,
|
||||
});
|
||||
}
|
||||
case FieldMetadataType.RATING: {
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import {
|
||||
isDefined,
|
||||
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties,
|
||||
} from 'twenty-shared/utils';
|
||||
|
||||
import { type DeleteOneFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/delete-field.input';
|
||||
import {
|
||||
FieldMetadataException,
|
||||
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 { 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';
|
||||
|
||||
type FromDeleteFieldInputToFlatFieldMetadatasToDeleteArgs = {
|
||||
existingFlatObjectMetadataMaps: FlatObjectMetadataMaps;
|
||||
deleteOneFieldInput: DeleteOneFieldInput;
|
||||
};
|
||||
export const fromDeleteFieldInputToFlatFieldMetadatasToDelete = ({
|
||||
existingFlatObjectMetadataMaps,
|
||||
deleteOneFieldInput: rawDeleteOneInput,
|
||||
}: FromDeleteFieldInputToFlatFieldMetadatasToDeleteArgs): FlatFieldMetadata[] => {
|
||||
const { id: fieldMetadataToDeleteId } =
|
||||
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties(
|
||||
rawDeleteOneInput,
|
||||
['id'],
|
||||
);
|
||||
|
||||
const flatFieldMetadataToDelete =
|
||||
findFlatFieldMetadataInFlatObjectMetadataMapsWithOnlyFieldId({
|
||||
fieldMetadataId: fieldMetadataToDeleteId,
|
||||
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(flatFieldMetadataToDelete)) {
|
||||
throw new FieldMetadataException(
|
||||
'Field to delete not found',
|
||||
FieldMetadataExceptionCode.FIELD_METADATA_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
isFlatFieldMetadataEntityOfType(
|
||||
flatFieldMetadataToDelete,
|
||||
FieldMetadataType.RELATION,
|
||||
)
|
||||
) {
|
||||
const { relationTargetFieldMetadataId, relationTargetObjectMetadataId } =
|
||||
flatFieldMetadataToDelete;
|
||||
|
||||
const relatedFlatObjectMetadata =
|
||||
existingFlatObjectMetadataMaps.byId[relationTargetObjectMetadataId];
|
||||
|
||||
if (!isDefined(relatedFlatObjectMetadata)) {
|
||||
throw new FieldMetadataException(
|
||||
`Deleted field metadata relation object metadata target not found`,
|
||||
FieldMetadataExceptionCode.OBJECT_METADATA_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
const relatedFlatFieldMetadata =
|
||||
relatedFlatObjectMetadata.fieldsById[relationTargetFieldMetadataId];
|
||||
|
||||
if (!isDefined(relatedFlatFieldMetadata)) {
|
||||
throw new FieldMetadataException(
|
||||
`Deleted field metadata relation field metadata target not found`,
|
||||
FieldMetadataExceptionCode.FIELD_METADATA_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
return [flatFieldMetadataToDelete, relatedFlatFieldMetadata];
|
||||
}
|
||||
|
||||
if (
|
||||
isFlatFieldMetadataEntityOfType(
|
||||
flatFieldMetadataToDelete,
|
||||
FieldMetadataType.MORPH_RELATION,
|
||||
)
|
||||
) {
|
||||
// TODO prastoin
|
||||
return [flatFieldMetadataToDelete];
|
||||
}
|
||||
|
||||
return [flatFieldMetadataToDelete];
|
||||
};
|
||||
+6
-2
@@ -39,14 +39,16 @@ const computeFieldMetadataRelationSettingsForRelationType = ({
|
||||
};
|
||||
|
||||
type FromRelationCreateFieldInputToFlatFieldMetadataArgs = {
|
||||
createFieldInput: CreateFieldInput;
|
||||
createFieldInput: Omit<CreateFieldInput, 'workspaceId'>;
|
||||
existingFlatObjectMetadataMaps: FlatObjectMetadataMaps;
|
||||
sourceParentFlatObjectMetadata: FlatObjectMetadata;
|
||||
workspaceId: string;
|
||||
};
|
||||
export const fromRelationCreateFieldInputToFlatFieldMetadata = async ({
|
||||
existingFlatObjectMetadataMaps,
|
||||
sourceParentFlatObjectMetadata,
|
||||
createFieldInput,
|
||||
workspaceId,
|
||||
}: FromRelationCreateFieldInputToFlatFieldMetadataArgs): Promise<
|
||||
FlatFieldMetadata[]
|
||||
> => {
|
||||
@@ -85,6 +87,7 @@ export const fromRelationCreateFieldInputToFlatFieldMetadata = async ({
|
||||
> = {
|
||||
...getDefaultFlatFieldMetadata({
|
||||
createFieldInput,
|
||||
workspaceId,
|
||||
fieldMetadataId: sourceRelationTargetFieldMetadataId,
|
||||
}),
|
||||
icon: createFieldInput.icon ?? 'IconRelationOneToMany',
|
||||
@@ -105,7 +108,7 @@ export const fromRelationCreateFieldInputToFlatFieldMetadata = async ({
|
||||
)}Id`,
|
||||
objectMetadataId: targetParentFlatObjectMetadata.id,
|
||||
type: FieldMetadataType.RELATION,
|
||||
workspaceId: createFieldInput.workspaceId,
|
||||
workspaceId,
|
||||
};
|
||||
const targetFlatFieldMetadataSettings =
|
||||
computeFieldMetadataRelationSettingsForRelationType({
|
||||
@@ -119,6 +122,7 @@ export const fromRelationCreateFieldInputToFlatFieldMetadata = async ({
|
||||
{
|
||||
...getDefaultFlatFieldMetadata({
|
||||
createFieldInput: targetCreateFieldInput,
|
||||
workspaceId,
|
||||
fieldMetadataId: targetRelationTargetFieldMetadataId,
|
||||
}),
|
||||
type: FieldMetadataType.RELATION,
|
||||
|
||||
+4
-2
@@ -6,11 +6,13 @@ import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-m
|
||||
|
||||
type GetDefaultFlatFieldMetadataArgs = {
|
||||
fieldMetadataId: string;
|
||||
createFieldInput: CreateFieldInput;
|
||||
createFieldInput: Omit<CreateFieldInput, 'workspaceId'>;
|
||||
workspaceId: string;
|
||||
};
|
||||
export const getDefaultFlatFieldMetadata = ({
|
||||
createFieldInput,
|
||||
fieldMetadataId,
|
||||
workspaceId,
|
||||
}: GetDefaultFlatFieldMetadataArgs) => {
|
||||
const { defaultValue, settings } = extractAndSanitizeObjectStringFields(
|
||||
createFieldInput,
|
||||
@@ -40,7 +42,7 @@ export const getDefaultFlatFieldMetadata = ({
|
||||
standardOverrides: null,
|
||||
type: createFieldInput.type,
|
||||
uniqueIdentifier: fieldMetadataId,
|
||||
workspaceId: createFieldInput.workspaceId,
|
||||
workspaceId,
|
||||
flatRelationTargetFieldMetadata: null,
|
||||
flatRelationTargetObjectMetadata: null,
|
||||
options: null,
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
|
||||
export const EMPTY_FLAT_OBJECT_METADATA_MAPS = {
|
||||
byId: {},
|
||||
idByNameSingular: {},
|
||||
} as const satisfies FlatObjectMetadataMaps;
|
||||
+2
-6
@@ -1,5 +1,6 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
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,
|
||||
@@ -17,11 +18,6 @@ export const extractFlatObjectMetadataMapsOutOfFlatObjectMetadataMapsOrThrow =
|
||||
flatObjectMetadataMaps: sourceFlatObjectMetadataMaps,
|
||||
objectMetadataIds,
|
||||
}: ExtractFlatObjectMetadataMapsOutOfFlatObjectMetadataMapsOrThrowArgs): FlatObjectMetadataMaps => {
|
||||
const emptyFlatObjectMetadataMaps: FlatObjectMetadataMaps = {
|
||||
byId: {},
|
||||
idByNameSingular: {},
|
||||
};
|
||||
|
||||
return objectMetadataIds.reduce(
|
||||
(flatObjectMetadataMaps, objectMetadataId) => {
|
||||
const flatObjectMetadataWithFlatFieldMaps =
|
||||
@@ -41,6 +37,6 @@ export const extractFlatObjectMetadataMapsOutOfFlatObjectMetadataMapsOrThrow =
|
||||
},
|
||||
);
|
||||
},
|
||||
emptyFlatObjectMetadataMaps,
|
||||
EMPTY_FLAT_OBJECT_METADATA_MAPS,
|
||||
);
|
||||
};
|
||||
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
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';
|
||||
|
||||
export type FindFlatFieldMetadataInFlatObjectMetadataMapsWithOnlyFieldIdArgs = {
|
||||
fieldMetadataId: string;
|
||||
flatObjectMetadataMaps: FlatObjectMetadataMaps;
|
||||
};
|
||||
// TODO add tests
|
||||
export const findFlatFieldMetadataInFlatObjectMetadataMapsWithOnlyFieldId = ({
|
||||
flatObjectMetadataMaps,
|
||||
fieldMetadataId,
|
||||
}: FindFlatFieldMetadataInFlatObjectMetadataMapsWithOnlyFieldIdArgs):
|
||||
| FlatFieldMetadata
|
||||
| undefined => {
|
||||
const flatObjectMetadataWithFlatFieldMapsArray = Object.values(
|
||||
flatObjectMetadataMaps.byId,
|
||||
).filter(isDefined);
|
||||
|
||||
const matchingFlatObjectMetadataWithFlatFieldMaps =
|
||||
flatObjectMetadataWithFlatFieldMapsArray.find(
|
||||
(flatObjectMetadataWithFlatFieldMaps) =>
|
||||
isDefined(
|
||||
flatObjectMetadataWithFlatFieldMaps.fieldsById[fieldMetadataId],
|
||||
),
|
||||
);
|
||||
|
||||
return matchingFlatObjectMetadataWithFlatFieldMaps?.fieldsById[
|
||||
fieldMetadataId
|
||||
];
|
||||
};
|
||||
-1
@@ -8,7 +8,6 @@ export const fromFlatObjectMetadataToFlatObjectMetadataWithFlatFieldMaps = (
|
||||
const emptyFlatObjectMetadataWithFlatFieldMaps: FlatObjectMetadataWithFlatFieldMaps =
|
||||
{
|
||||
...flatObjectMetadata,
|
||||
flatIndexMetadatas: [],
|
||||
flatFieldMetadatas: [],
|
||||
fieldIdByJoinColumnName: {},
|
||||
fieldIdByName: {},
|
||||
|
||||
+2
-7
@@ -1,21 +1,16 @@
|
||||
import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
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 { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
export const fromFlatObjectMetadatasToFlatObjectMetadataMaps = (
|
||||
flatObjectMetadatas: FlatObjectMetadata[],
|
||||
) => {
|
||||
const emptyFlatObjectMetadataMaps: FlatObjectMetadataMaps = {
|
||||
byId: {},
|
||||
idByNameSingular: {},
|
||||
};
|
||||
|
||||
return flatObjectMetadatas.reduce(
|
||||
(flatObjectMetadataMaps, flatObjectMetadata) =>
|
||||
addFlatObjectMetadataToFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadata,
|
||||
flatObjectMetadataMaps,
|
||||
}),
|
||||
emptyFlatObjectMetadataMaps,
|
||||
EMPTY_FLAT_OBJECT_METADATA_MAPS,
|
||||
);
|
||||
};
|
||||
|
||||
+35
-40
@@ -4,17 +4,12 @@ import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
import { fromCreateObjectInputToFlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-create-object-input-to-flat-object-metadata.util';
|
||||
import { fromFlatObjectMetadataMapsToFlatObjectMetadatas } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-flat-object-metadata-maps-to-flat-object-metadatas.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 { ObjectMetadataEntity } from './object-metadata.entity';
|
||||
|
||||
import { type CreateObjectInput } from './dtos/create-object.input';
|
||||
|
||||
@Injectable()
|
||||
export class ObjectMetadataServiceV2 extends TypeOrmQueryService<ObjectMetadataEntity> {
|
||||
constructor(
|
||||
@@ -27,43 +22,43 @@ export class ObjectMetadataServiceV2 extends TypeOrmQueryService<ObjectMetadataE
|
||||
super(objectMetadataRepository);
|
||||
}
|
||||
|
||||
override async createOne(
|
||||
objectMetadataInput: CreateObjectInput,
|
||||
): Promise<ObjectMetadataEntity> {
|
||||
const { flatObjectMetadataMaps } =
|
||||
await this.workspaceMetadataCacheService.getExistingOrRecomputeFlatObjectMetadataMaps(
|
||||
{
|
||||
workspaceId: objectMetadataInput.workspaceId,
|
||||
},
|
||||
);
|
||||
// override async createOne(
|
||||
// objectMetadataInput: CreateObjectInput,
|
||||
// ): Promise<ObjectMetadataEntity> {
|
||||
// const { flatObjectMetadataMaps } =
|
||||
// await this.workspaceMetadataCacheService.getExistingOrRecomputeFlatObjectMetadataMaps(
|
||||
// {
|
||||
// workspaceId: objectMetadataInput.workspaceId,
|
||||
// },
|
||||
// );
|
||||
|
||||
const createdRawFlatObjectMetadata =
|
||||
fromCreateObjectInputToFlatObjectMetadata(objectMetadataInput);
|
||||
const existingFlatObjectMetadatas: FlatObjectMetadataMaps = {
|
||||
byId: {},
|
||||
idByNameSingular: {},
|
||||
};
|
||||
// @ts-expect-error TODO implement validateFlatObjectMetadata
|
||||
const createdFlatObjectMetadata = validateFlatObjectMetadata({
|
||||
existing:
|
||||
// Here we assume that EVERYTHING is in cache and up to date, this is very critical, also race condition prone :thinking:
|
||||
fromFlatObjectMetadataMapsToFlatObjectMetadatas(flatObjectMetadataMaps),
|
||||
toValidate: [createdRawFlatObjectMetadata],
|
||||
});
|
||||
// const createdRawFlatObjectMetadata =
|
||||
// fromCreateObjectInputToFlatObjectMetadata(objectMetadataInput);
|
||||
// const existingFlatObjectMetadatas: FlatObjectMetadataMaps = {
|
||||
// byId: {},
|
||||
// idByNameSingular: {},
|
||||
// };
|
||||
// // @ts-expect-error TODO implement validateFlatObjectMetadata
|
||||
// const createdFlatObjectMetadata = validateFlatObjectMetadata({
|
||||
// existing:
|
||||
// // Here we assume that EVERYTHING is in cache and up to date, this is very critical, also race condition prone :thinking:
|
||||
// fromFlatObjectMetadataMapsToFlatObjectMetadatas(flatObjectMetadataMaps),
|
||||
// toValidate: [createdRawFlatObjectMetadata],
|
||||
// });
|
||||
|
||||
const workspaceMigration = this.workspaceMigrationBuilderV2.build({
|
||||
objectMetadataFromToInputs: {
|
||||
from: fromFlatObjectMetadataMapsToFlatObjectMetadatas(
|
||||
existingFlatObjectMetadatas,
|
||||
),
|
||||
to: [createdFlatObjectMetadata],
|
||||
},
|
||||
inferDeletionFromMissingObjectFieldIndex: false,
|
||||
workspaceId: objectMetadataInput.workspaceId,
|
||||
});
|
||||
// const workspaceMigration = this.workspaceMigrationBuilderV2.build({
|
||||
// objectMetadataFromToInputs: {
|
||||
// from: fromFlatObjectMetadataMapsToFlatObjectMetadatas(
|
||||
// existingFlatObjectMetadatas,
|
||||
// ),
|
||||
// to: [createdFlatObjectMetadata],
|
||||
// },
|
||||
// inferDeletionFromMissingObjectFieldIndex: false,
|
||||
// workspaceId: objectMetadataInput.workspaceId,
|
||||
// });
|
||||
|
||||
await this.workspaceMigrationRunnerV2Service.run(workspaceMigration);
|
||||
// await this.workspaceMigrationRunnerV2Service.run(workspaceMigration);
|
||||
|
||||
return createdFlatObjectMetadata; // TODO retrieve from cache
|
||||
}
|
||||
// return createdFlatObjectMetadata; // TODO retrieve from cache
|
||||
// }
|
||||
}
|
||||
|
||||
+1126
-95
File diff suppressed because it is too large
Load Diff
+80
-196
@@ -3,19 +3,20 @@ import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
|
||||
|
||||
import { COMPANY_FLAT_FIELDS_MOCK } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/company-flat-fields.mock';
|
||||
import { getFlatFieldMetadataMock } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/get-flat-field-metadata.mock';
|
||||
import { OPPORTUNITY_FLAT_FIELDS_MOCK } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/opportunity-flat-fields.mock';
|
||||
import { PET_FLAT_FIELDS_MOCK } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/pet-flat-fields.mock';
|
||||
import { FLAT_OBJECT_METADATA_MAPS_MOCKS } from 'src/engine/metadata-modules/flat-object-metadata-maps/mocks/flat-object-metadata-maps.mock';
|
||||
import { addFlatFieldMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/add-flat-field-metadata-in-flat-object-metadata-maps-or-throw.util';
|
||||
import { deleteFieldFromFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/delete-field-from-flat-object-metadata-maps-or-throw.util';
|
||||
import { replaceFlatFieldMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/replace-flat-field-metadata-in-flat-object-metadata-maps-or-throw.util';
|
||||
import { COMPANY_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/company-flat-object.mock';
|
||||
import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/get-flat-object-metadata.mock';
|
||||
import { PET_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/pet-flat-object.mock';
|
||||
import { ROCKET_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/rocket-flat-object.mock';
|
||||
import { type WorkspaceMigrationBuilderTestCase } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/types/workspace-migration-builder-test-case.type';
|
||||
|
||||
const basicObjectMetadataId = faker.string.uuid();
|
||||
const basicFlatFieldMetadatas = Array.from({ length: 5 }, (_value, index) =>
|
||||
getFlatFieldMetadataMock({
|
||||
objectMetadataId: basicObjectMetadataId,
|
||||
uniqueIdentifier: `field_${index}`,
|
||||
type: FieldMetadataType.TEXT,
|
||||
}),
|
||||
);
|
||||
|
||||
// TODO prastoin test defaultValue and settings updates
|
||||
// TODO prastoin test standard abstraction in TDD style
|
||||
|
||||
@@ -24,16 +25,14 @@ const relationTestCases: WorkspaceMigrationBuilderTestCase[] = [
|
||||
title: 'It should build an create_field action for a RELATION field',
|
||||
context: {
|
||||
input: () => {
|
||||
const objectMetadataId = faker.string.uuid();
|
||||
const targetObjectMetadataId = faker.string.uuid();
|
||||
const createdFlatRelationFieldMetadata = getFlatFieldMetadataMock({
|
||||
uniqueIdentifier: 'field-metadata-unique-identifier-1',
|
||||
objectMetadataId,
|
||||
objectMetadataId: PET_FLAT_OBJECT_MOCK.id,
|
||||
type: FieldMetadataType.RELATION,
|
||||
relationTargetFieldMetadataId: faker.string.uuid(),
|
||||
relationTargetObjectMetadataId: faker.string.uuid(),
|
||||
flatRelationTargetFieldMetadata: getFlatFieldMetadataMock({
|
||||
objectMetadataId: targetObjectMetadataId,
|
||||
objectMetadataId: COMPANY_FLAT_OBJECT_MOCK.id,
|
||||
type: FieldMetadataType.RELATION,
|
||||
uniqueIdentifier: 'field-metadata-unique-identifier-2',
|
||||
}),
|
||||
@@ -41,20 +40,14 @@ const relationTestCases: WorkspaceMigrationBuilderTestCase[] = [
|
||||
uniqueIdentifier: 'object-metadata-unique-identifier-2',
|
||||
}),
|
||||
});
|
||||
const flatObjectMetadata = getFlatObjectMetadataMock({
|
||||
uniqueIdentifier: 'object-metadata-unique-identifier-1',
|
||||
isLabelSyncedWithName: true,
|
||||
flatFieldMetadatas: [],
|
||||
});
|
||||
|
||||
return {
|
||||
from: [flatObjectMetadata],
|
||||
to: [
|
||||
{
|
||||
...flatObjectMetadata,
|
||||
flatFieldMetadatas: [createdFlatRelationFieldMetadata],
|
||||
},
|
||||
],
|
||||
fromFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
toFlatObjectMetadataMaps:
|
||||
addFlatFieldMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
flatFieldMetadata: createdFlatRelationFieldMetadata,
|
||||
}),
|
||||
};
|
||||
},
|
||||
expectedActionsTypeCounter: {
|
||||
@@ -65,42 +58,17 @@ const relationTestCases: WorkspaceMigrationBuilderTestCase[] = [
|
||||
{
|
||||
title: 'It should build an update_field action for a RELATION field',
|
||||
context: {
|
||||
input: () => {
|
||||
const objectMetadataId = faker.string.uuid();
|
||||
|
||||
const updatedFieldMetadata = getFlatFieldMetadataMock({
|
||||
uniqueIdentifier: 'field-metadata-unique-identifier-1',
|
||||
objectMetadataId,
|
||||
type: FieldMetadataType.RELATION,
|
||||
relationTargetFieldMetadataId: faker.string.uuid(),
|
||||
relationTargetObjectMetadataId: faker.string.uuid(),
|
||||
});
|
||||
const flatObjectMetadata = getFlatObjectMetadataMock({
|
||||
uniqueIdentifier: 'object-metadata-unique-identifier-1',
|
||||
isLabelSyncedWithName: true,
|
||||
flatFieldMetadatas: [
|
||||
...basicFlatFieldMetadatas,
|
||||
updatedFieldMetadata,
|
||||
],
|
||||
});
|
||||
|
||||
return {
|
||||
from: [flatObjectMetadata],
|
||||
to: [
|
||||
{
|
||||
...flatObjectMetadata,
|
||||
flatFieldMetadatas: [
|
||||
...basicFlatFieldMetadatas,
|
||||
{
|
||||
...updatedFieldMetadata,
|
||||
isActive: false,
|
||||
description: 'new description',
|
||||
label: 'new label',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
input: {
|
||||
fromFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
toFlatObjectMetadataMaps:
|
||||
replaceFlatFieldMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatFieldMetadata: getFlatFieldMetadataMock({
|
||||
...PET_FLAT_FIELDS_MOCK.species,
|
||||
description: 'new description',
|
||||
label: 'new label',
|
||||
}),
|
||||
flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
}),
|
||||
},
|
||||
expectedActionsTypeCounter: {
|
||||
updateField: 1,
|
||||
@@ -111,47 +79,23 @@ const relationTestCases: WorkspaceMigrationBuilderTestCase[] = [
|
||||
title:
|
||||
'It should NOT build an update_field action for a field RELATION uncovered fields mutation',
|
||||
context: {
|
||||
input: () => {
|
||||
const objectMetadataId = faker.string.uuid();
|
||||
const updatedFieldMetadata = getFlatFieldMetadataMock({
|
||||
uniqueIdentifier: 'field-metadata-unique-identifier-1',
|
||||
objectMetadataId,
|
||||
type: FieldMetadataType.RELATION,
|
||||
settings: {
|
||||
relationType: RelationType.MANY_TO_ONE,
|
||||
joinColumnName: 'column-name',
|
||||
onDelete: undefined,
|
||||
},
|
||||
relationTargetFieldMetadataId: faker.string.uuid(),
|
||||
relationTargetObjectMetadataId: faker.string.uuid(),
|
||||
});
|
||||
const flatObjectMetadata = getFlatObjectMetadataMock({
|
||||
uniqueIdentifier: 'object-metadata-unique-identifier-1',
|
||||
isLabelSyncedWithName: true,
|
||||
flatFieldMetadatas: [updatedFieldMetadata],
|
||||
});
|
||||
|
||||
return {
|
||||
from: [flatObjectMetadata],
|
||||
to: [
|
||||
{
|
||||
...flatObjectMetadata,
|
||||
flatFieldMetadatas: [
|
||||
{
|
||||
...updatedFieldMetadata,
|
||||
settings: {
|
||||
relationType: RelationType.ONE_TO_MANY,
|
||||
isForeignKey: false,
|
||||
joinColumnName: 'new-column-name',
|
||||
onDelete: undefined,
|
||||
},
|
||||
relationTargetFieldMetadataId: faker.string.uuid(),
|
||||
relationTargetObjectMetadataId: faker.string.uuid(),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
input: {
|
||||
fromFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
toFlatObjectMetadataMaps:
|
||||
replaceFlatFieldMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatFieldMetadata: getFlatFieldMetadataMock({
|
||||
...COMPANY_FLAT_FIELDS_MOCK.opportunities,
|
||||
settings: {
|
||||
relationType: RelationType.MANY_TO_ONE,
|
||||
joinColumnName: 'new-column-name',
|
||||
onDelete: undefined,
|
||||
},
|
||||
name: 'newName',
|
||||
relationTargetFieldMetadataId: faker.string.uuid(),
|
||||
relationTargetObjectMetadataId: faker.string.uuid(),
|
||||
}),
|
||||
flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -161,29 +105,17 @@ const basicCrudTestCases: WorkspaceMigrationBuilderTestCase[] = [
|
||||
{
|
||||
title: 'It should build an create_field action',
|
||||
context: {
|
||||
input: () => {
|
||||
const objectMetadataId = faker.string.uuid();
|
||||
|
||||
const flatFieldMetadata = getFlatFieldMetadataMock({
|
||||
uniqueIdentifier: 'field-metadata-unique-identifier-1',
|
||||
type: FieldMetadataType.TEXT,
|
||||
objectMetadataId,
|
||||
});
|
||||
const flatObjectMetadata = getFlatObjectMetadataMock({
|
||||
uniqueIdentifier: 'object-metadata-unique-identifier-1',
|
||||
isLabelSyncedWithName: true,
|
||||
flatFieldMetadatas: [],
|
||||
});
|
||||
|
||||
return {
|
||||
from: [flatObjectMetadata],
|
||||
to: [
|
||||
{
|
||||
...flatObjectMetadata,
|
||||
flatFieldMetadatas: [flatFieldMetadata],
|
||||
},
|
||||
],
|
||||
};
|
||||
input: {
|
||||
fromFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
toFlatObjectMetadataMaps:
|
||||
addFlatFieldMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatFieldMetadata: getFlatFieldMetadataMock({
|
||||
uniqueIdentifier: 'field-metadata-unique-identifier-1',
|
||||
type: FieldMetadataType.TEXT,
|
||||
objectMetadataId: ROCKET_FLAT_OBJECT_MOCK.id,
|
||||
}),
|
||||
flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
}),
|
||||
},
|
||||
expectedActionsTypeCounter: {
|
||||
createField: 1,
|
||||
@@ -193,37 +125,19 @@ const basicCrudTestCases: WorkspaceMigrationBuilderTestCase[] = [
|
||||
{
|
||||
title: 'It should build an update_field action',
|
||||
context: {
|
||||
input: () => {
|
||||
const objectMetadataId = faker.string.uuid();
|
||||
|
||||
const flatFieldMetadata = getFlatFieldMetadataMock({
|
||||
uniqueIdentifier: 'field-metadata-unique-identifier-1',
|
||||
type: FieldMetadataType.TEXT,
|
||||
objectMetadataId,
|
||||
});
|
||||
const flatObjectMetadata = getFlatObjectMetadataMock({
|
||||
uniqueIdentifier: 'object-metadata-unique-identifier-1',
|
||||
flatFieldMetadatas: [...basicFlatFieldMetadatas, flatFieldMetadata],
|
||||
});
|
||||
|
||||
return {
|
||||
from: [flatObjectMetadata],
|
||||
to: [
|
||||
{
|
||||
...flatObjectMetadata,
|
||||
flatFieldMetadatas: [
|
||||
...basicFlatFieldMetadatas,
|
||||
{
|
||||
...flatFieldMetadata,
|
||||
description: 'new description',
|
||||
name: 'new name',
|
||||
isActive: false,
|
||||
icon: 'new icon',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
input: {
|
||||
fromFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
toFlatObjectMetadataMaps:
|
||||
replaceFlatFieldMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
flatFieldMetadata: getFlatFieldMetadataMock({
|
||||
...OPPORTUNITY_FLAT_FIELDS_MOCK.stage,
|
||||
description: 'new description',
|
||||
name: 'new name',
|
||||
isActive: false,
|
||||
icon: 'new icon',
|
||||
}),
|
||||
}),
|
||||
},
|
||||
expectedActionsTypeCounter: {
|
||||
updateField: 1,
|
||||
@@ -233,28 +147,13 @@ const basicCrudTestCases: WorkspaceMigrationBuilderTestCase[] = [
|
||||
{
|
||||
title: 'It should build a delete_field action',
|
||||
context: {
|
||||
input: () => {
|
||||
const objectMetadataId = faker.string.uuid();
|
||||
|
||||
const flatFieldMetadata = getFlatFieldMetadataMock({
|
||||
uniqueIdentifier: 'field-metadata-unique-identifier-1',
|
||||
type: FieldMetadataType.TEXT,
|
||||
objectMetadataId,
|
||||
});
|
||||
const flatObjectMetadata = getFlatObjectMetadataMock({
|
||||
uniqueIdentifier: 'object-metadata-unique-identifier-1',
|
||||
flatFieldMetadatas: [...basicFlatFieldMetadatas, flatFieldMetadata],
|
||||
});
|
||||
|
||||
return {
|
||||
from: [flatObjectMetadata],
|
||||
to: [
|
||||
{
|
||||
...flatObjectMetadata,
|
||||
flatFieldMetadatas: basicFlatFieldMetadatas,
|
||||
},
|
||||
],
|
||||
};
|
||||
input: {
|
||||
fromFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
toFlatObjectMetadataMaps: deleteFieldFromFlatObjectMetadataMapsOrThrow({
|
||||
fieldMetadataId: PET_FLAT_FIELDS_MOCK.species.id,
|
||||
objectMetadataId: PET_FLAT_FIELDS_MOCK.species.objectMetadataId,
|
||||
flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
}),
|
||||
},
|
||||
expectedActionsTypeCounter: {
|
||||
deleteField: 1,
|
||||
@@ -271,24 +170,9 @@ export const WORKSPACE_MIGRATION_FIELD_BUILDER_TEST_CASES: WorkspaceMigrationBui
|
||||
title:
|
||||
'It should not infer any actions as from and to fields are identical',
|
||||
context: {
|
||||
input: () => {
|
||||
const objectMetadataId = faker.string.uuid();
|
||||
const flatFieldMetadata = getFlatFieldMetadataMock({
|
||||
uniqueIdentifier: 'field-metadata-unique-identifier-1',
|
||||
type: FieldMetadataType.TEXT,
|
||||
objectMetadataId,
|
||||
});
|
||||
const from = [
|
||||
getFlatObjectMetadataMock({
|
||||
uniqueIdentifier: 'object-metadata-unique-identifier-1',
|
||||
flatFieldMetadatas: [flatFieldMetadata],
|
||||
}),
|
||||
];
|
||||
|
||||
return {
|
||||
from,
|
||||
to: from,
|
||||
};
|
||||
input: {
|
||||
fromFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
toFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
+57
-74
@@ -1,37 +1,30 @@
|
||||
import { faker } from '@faker-js/faker';
|
||||
|
||||
import { getFlatIndexMetadataMock } from 'src/engine/metadata-modules/flat-index-metadata/__mocks__/get-flat-index-metadata.mock';
|
||||
import { FLAT_OBJECT_METADATA_MAPS_MOCKS } from 'src/engine/metadata-modules/flat-object-metadata-maps/mocks/flat-object-metadata-maps.mock';
|
||||
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 { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/get-flat-object-metadata.mock';
|
||||
import { NOTE_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/note-flat-object.mock';
|
||||
import { type WorkspaceMigrationBuilderTestCase } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/types/workspace-migration-builder-test-case.type';
|
||||
|
||||
// Should test more things such as flatFieldIndex diffing
|
||||
const objectMetadataId = faker.string.uuid();
|
||||
|
||||
export const WORKSPACE_MIGRATION_INDEX_BUILDER_TEST_CASES: WorkspaceMigrationBuilderTestCase[] =
|
||||
[
|
||||
{
|
||||
title: 'It should build an create_index action',
|
||||
context: {
|
||||
input: () => {
|
||||
const flatIndexMetadata = getFlatIndexMetadataMock({
|
||||
uniqueIdentifier: 'field-metadata-unique-identifier-1',
|
||||
objectMetadataId,
|
||||
});
|
||||
const flatObjectMetadata = getFlatObjectMetadataMock({
|
||||
uniqueIdentifier: 'object-metadata-unique-identifier-1',
|
||||
isLabelSyncedWithName: true,
|
||||
flatIndexMetadatas: [],
|
||||
});
|
||||
|
||||
return {
|
||||
from: [flatObjectMetadata],
|
||||
to: [
|
||||
{
|
||||
...flatObjectMetadata,
|
||||
flatIndexMetadatas: [flatIndexMetadata],
|
||||
},
|
||||
],
|
||||
};
|
||||
input: {
|
||||
fromFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
toFlatObjectMetadataMaps:
|
||||
replaceFlatObjectMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
flatObjectMetadata: getFlatObjectMetadataMock({
|
||||
...NOTE_FLAT_OBJECT_MOCK,
|
||||
flatIndexMetadatas: [
|
||||
getFlatIndexMetadataMock({
|
||||
objectMetadataId: NOTE_FLAT_OBJECT_MOCK.id,
|
||||
uniqueIdentifier: 'field-metadata-unique-identifier-1',
|
||||
}),
|
||||
],
|
||||
}),
|
||||
}),
|
||||
},
|
||||
expectedActionsTypeCounter: {
|
||||
createIndex: 1,
|
||||
@@ -44,30 +37,37 @@ export const WORKSPACE_MIGRATION_INDEX_BUILDER_TEST_CASES: WorkspaceMigrationBui
|
||||
context: {
|
||||
input: () => {
|
||||
const flatIndexMetadata = getFlatIndexMetadataMock({
|
||||
objectMetadataId: NOTE_FLAT_OBJECT_MOCK.id,
|
||||
uniqueIdentifier: 'field-metadata-unique-identifier-1',
|
||||
objectMetadataId,
|
||||
});
|
||||
const flatObjectMetadata = getFlatObjectMetadataMock({
|
||||
uniqueIdentifier: 'object-metadata-unique-identifier-1',
|
||||
isLabelSyncedWithName: true,
|
||||
flatIndexMetadatas: [flatIndexMetadata],
|
||||
});
|
||||
|
||||
return {
|
||||
from: [flatObjectMetadata],
|
||||
to: [
|
||||
{
|
||||
...flatObjectMetadata,
|
||||
const fromFlatObjectMetadataMaps =
|
||||
replaceFlatObjectMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
flatObjectMetadata: getFlatObjectMetadataMock({
|
||||
...NOTE_FLAT_OBJECT_MOCK,
|
||||
flatIndexMetadatas: [flatIndexMetadata],
|
||||
}),
|
||||
});
|
||||
const toFlatObjectMetadataMaps =
|
||||
replaceFlatObjectMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
flatObjectMetadata: getFlatObjectMetadataMock({
|
||||
...NOTE_FLAT_OBJECT_MOCK,
|
||||
flatIndexMetadatas: [
|
||||
{
|
||||
...flatIndexMetadata,
|
||||
name: 'new index name',
|
||||
isUnique: false,
|
||||
indexWhereClause: 'new index where clause',
|
||||
uniqueIdentifier: 'field-metadata-unique-identifier-1',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
return {
|
||||
fromFlatObjectMetadataMaps,
|
||||
toFlatObjectMetadataMaps,
|
||||
};
|
||||
},
|
||||
expectedActionsTypeCounter: {
|
||||
@@ -79,26 +79,21 @@ export const WORKSPACE_MIGRATION_INDEX_BUILDER_TEST_CASES: WorkspaceMigrationBui
|
||||
{
|
||||
title: 'It should build a delete_index action',
|
||||
context: {
|
||||
input: () => {
|
||||
const flatIndexMetadata = getFlatIndexMetadataMock({
|
||||
uniqueIdentifier: 'field-metadata-unique-identifier-1',
|
||||
objectMetadataId,
|
||||
});
|
||||
const flatObjectMetadata = getFlatObjectMetadataMock({
|
||||
uniqueIdentifier: 'object-metadata-unique-identifier-1',
|
||||
isLabelSyncedWithName: true,
|
||||
flatIndexMetadatas: [flatIndexMetadata],
|
||||
});
|
||||
|
||||
return {
|
||||
from: [flatObjectMetadata],
|
||||
to: [
|
||||
{
|
||||
...flatObjectMetadata,
|
||||
flatIndexMetadatas: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
input: {
|
||||
fromFlatObjectMetadataMaps:
|
||||
replaceFlatObjectMetadataInFlatObjectMetadataMapsOrThrow({
|
||||
flatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
flatObjectMetadata: getFlatObjectMetadataMock({
|
||||
...NOTE_FLAT_OBJECT_MOCK,
|
||||
flatIndexMetadatas: [
|
||||
getFlatIndexMetadataMock({
|
||||
objectMetadataId: NOTE_FLAT_OBJECT_MOCK.id,
|
||||
uniqueIdentifier: 'field-metadata-unique-identifier-1',
|
||||
}),
|
||||
],
|
||||
}),
|
||||
}),
|
||||
toFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
},
|
||||
expectedActionsTypeCounter: {
|
||||
deleteIndex: 1,
|
||||
@@ -109,21 +104,9 @@ export const WORKSPACE_MIGRATION_INDEX_BUILDER_TEST_CASES: WorkspaceMigrationBui
|
||||
title:
|
||||
'It should not infer any actions as from and to indexes are identical',
|
||||
context: {
|
||||
input: () => {
|
||||
const flatIndexMetadata = getFlatIndexMetadataMock({
|
||||
uniqueIdentifier: 'field-metadata-unique-identifier-1',
|
||||
objectMetadataId,
|
||||
});
|
||||
const flatObjectMetadata = getFlatObjectMetadataMock({
|
||||
uniqueIdentifier: 'object-metadata-unique-identifier-1',
|
||||
isLabelSyncedWithName: true,
|
||||
flatIndexMetadatas: [flatIndexMetadata],
|
||||
});
|
||||
|
||||
return {
|
||||
from: [flatObjectMetadata],
|
||||
to: [flatObjectMetadata],
|
||||
};
|
||||
input: {
|
||||
fromFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
toFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
+53
-83
@@ -1,9 +1,13 @@
|
||||
import { faker } from '@faker-js/faker';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
import { getFlatFieldMetadataMock } from 'src/engine/metadata-modules/flat-field-metadata/__mocks__/get-flat-field-metadata.mock';
|
||||
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';
|
||||
import { COMPANY_FLAT_OBJECT_MOCK } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/company-flat-object.mock';
|
||||
import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/get-flat-object-metadata.mock';
|
||||
import { 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';
|
||||
import { type WorkspaceMigrationBuilderTestCase } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/__tests__/types/workspace-migration-builder-test-case.type';
|
||||
|
||||
export const WORKSPACE_MIGRATION_OBJECT_BUILDER_TEST_CASES: WorkspaceMigrationBuilderTestCase[] =
|
||||
@@ -12,25 +16,18 @@ export const WORKSPACE_MIGRATION_OBJECT_BUILDER_TEST_CASES: WorkspaceMigrationBu
|
||||
title:
|
||||
'It should build an update_object action with all object updated fields',
|
||||
context: {
|
||||
input: () => {
|
||||
const flatObjectMetadata = getFlatObjectMetadataMock({
|
||||
uniqueIdentifier: 'pomme',
|
||||
nameSingular: 'toto',
|
||||
namePlural: 'totos',
|
||||
isLabelSyncedWithName: true,
|
||||
});
|
||||
|
||||
return {
|
||||
from: [flatObjectMetadata],
|
||||
to: [
|
||||
{
|
||||
...flatObjectMetadata,
|
||||
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,
|
||||
@@ -40,18 +37,16 @@ export const WORKSPACE_MIGRATION_OBJECT_BUILDER_TEST_CASES: WorkspaceMigrationBu
|
||||
{
|
||||
title: 'It should build a create_object action',
|
||||
context: {
|
||||
input: () => {
|
||||
const flatObjectMetadata = getFlatObjectMetadataMock({
|
||||
uniqueIdentifier: 'pomme',
|
||||
nameSingular: 'toto',
|
||||
namePlural: 'totos',
|
||||
isLabelSyncedWithName: true,
|
||||
});
|
||||
|
||||
return {
|
||||
from: [],
|
||||
to: [flatObjectMetadata],
|
||||
};
|
||||
input: {
|
||||
fromFlatObjectMetadataMaps:
|
||||
fromFlatObjectMetadatasToFlatObjectMetadataMaps([
|
||||
PET_FLAT_OBJECT_MOCK,
|
||||
]),
|
||||
toFlatObjectMetadataMaps:
|
||||
fromFlatObjectMetadatasToFlatObjectMetadataMaps([
|
||||
PET_FLAT_OBJECT_MOCK,
|
||||
ROCKET_FLAT_OBJECT_MOCK,
|
||||
]),
|
||||
},
|
||||
expectedActionsTypeCounter: {
|
||||
createObject: 1,
|
||||
@@ -60,37 +55,22 @@ export const WORKSPACE_MIGRATION_OBJECT_BUILDER_TEST_CASES: WorkspaceMigrationBu
|
||||
},
|
||||
{
|
||||
title:
|
||||
'It should build a create_object and create_field and create_index actions for each of this fieldMetadata',
|
||||
'It should build a create_object and create_index actions for each of this fieldMetadata',
|
||||
context: {
|
||||
input: () => {
|
||||
const objectMetadataId = faker.string.uuid();
|
||||
const flatFieldMetadatas = Array.from(
|
||||
{ length: 5 },
|
||||
(_value, index) =>
|
||||
getFlatFieldMetadataMock({
|
||||
type: FieldMetadataType.TEXT,
|
||||
objectMetadataId,
|
||||
uniqueIdentifier: `field_${index}`,
|
||||
}),
|
||||
);
|
||||
const flatIndexMetadata = getFlatIndexMetadataMock({
|
||||
uniqueIdentifier: 'field-metadata-unique-identifier-1',
|
||||
objectMetadataId,
|
||||
});
|
||||
const flatObjectMetadata = getFlatObjectMetadataMock({
|
||||
uniqueIdentifier: 'pomme',
|
||||
nameSingular: 'toto',
|
||||
namePlural: 'totos',
|
||||
isLabelSyncedWithName: true,
|
||||
id: objectMetadataId,
|
||||
flatFieldMetadatas,
|
||||
flatIndexMetadatas: [flatIndexMetadata],
|
||||
});
|
||||
|
||||
return {
|
||||
from: [],
|
||||
to: [flatObjectMetadata],
|
||||
};
|
||||
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: {
|
||||
@@ -103,36 +83,26 @@ export const WORKSPACE_MIGRATION_OBJECT_BUILDER_TEST_CASES: WorkspaceMigrationBu
|
||||
{
|
||||
title: 'It should build a delete_object action',
|
||||
context: {
|
||||
input: () => {
|
||||
const flatObjectMetadata = getFlatObjectMetadataMock({
|
||||
uniqueIdentifier: 'pomme',
|
||||
nameSingular: 'toto',
|
||||
namePlural: 'totos',
|
||||
isLabelSyncedWithName: true,
|
||||
});
|
||||
|
||||
return {
|
||||
from: [flatObjectMetadata],
|
||||
to: [],
|
||||
};
|
||||
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,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'It should not infer any actions as from and to are identical',
|
||||
context: {
|
||||
input: () => {
|
||||
const from = [
|
||||
getFlatObjectMetadataMock({ uniqueIdentifier: 'pomme' }),
|
||||
];
|
||||
|
||||
return {
|
||||
from,
|
||||
to: from,
|
||||
};
|
||||
input: {
|
||||
fromFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
toFlatObjectMetadataMaps: FLAT_OBJECT_METADATA_MAPS_MOCKS,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
+4
-6
@@ -2,11 +2,7 @@ import { type EachTestingContext } from 'twenty-shared/testing';
|
||||
|
||||
import { type ConvertActionTypeToCamelCase } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/convert-action-type-to-camel-case.type';
|
||||
import { type WorkspaceMigrationActionTypeV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-action-common-v2';
|
||||
import { type WorkspaceMigrationBuilderV2Service } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-builder-v2.service';
|
||||
|
||||
type WorkspaceBuilderArgs = Parameters<
|
||||
typeof WorkspaceMigrationBuilderV2Service.prototype.build
|
||||
>[0]['objectMetadataFromToInputs'];
|
||||
import { type WorkspaceMigrationBuildArgs } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/workspace-migration-builder-v2.service';
|
||||
|
||||
export type CamelCasedWorkspaceMigrationActionsType =
|
||||
ConvertActionTypeToCamelCase<WorkspaceMigrationActionTypeV2>;
|
||||
@@ -16,6 +12,8 @@ export type ExpectedActionCounters = Partial<
|
||||
>;
|
||||
|
||||
export type WorkspaceMigrationBuilderTestCase = EachTestingContext<{
|
||||
input: WorkspaceBuilderArgs | (() => WorkspaceBuilderArgs);
|
||||
input:
|
||||
| Omit<WorkspaceMigrationBuildArgs, 'workspaceId'>
|
||||
| (() => Omit<WorkspaceMigrationBuildArgs, 'workspaceId'>);
|
||||
expectedActionsTypeCounter?: ExpectedActionCounters;
|
||||
}>;
|
||||
|
||||
+9
-5
@@ -31,6 +31,7 @@ const allWorkspaceBuilderTestCases: {
|
||||
},
|
||||
];
|
||||
|
||||
// TODO prastoin add coverage to infer deletion from missing entities
|
||||
const expectedActionsTypeCounterChecker = ({
|
||||
expectedActionsTypeCounter,
|
||||
workspaceMigration,
|
||||
@@ -81,12 +82,15 @@ describe.each(allWorkspaceBuilderTestCases)(
|
||||
it.each(eachTestingContextFilter(testCases))(
|
||||
'$title',
|
||||
({ context: { input, expectedActionsTypeCounter } }) => {
|
||||
const { from, to } = typeof input === 'function' ? input() : input;
|
||||
const {
|
||||
fromFlatObjectMetadataMaps,
|
||||
toFlatObjectMetadataMaps,
|
||||
inferDeletionFromMissingObjectFieldIndex,
|
||||
} = typeof input === 'function' ? input() : input;
|
||||
const workspaceMigration = service.build({
|
||||
objectMetadataFromToInputs: {
|
||||
from,
|
||||
to,
|
||||
},
|
||||
fromFlatObjectMetadataMaps,
|
||||
toFlatObjectMetadataMaps,
|
||||
inferDeletionFromMissingObjectFieldIndex,
|
||||
workspaceId: '20202020-52cc-4c64-ad63-76c26fc3a1e1',
|
||||
});
|
||||
|
||||
|
||||
+20
-8
@@ -2,7 +2,8 @@ import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { type FromTo } from 'twenty-shared/types';
|
||||
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
import { fromFlatObjectMetadataMapsToFlatObjectMetadatas } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-flat-object-metadata-maps-to-flat-object-metadatas.util';
|
||||
import { deletedCreatedUpdatedMatrixDispatcher } from 'src/engine/workspace-manager/workspace-migration-v2/utils/deleted-created-updated-matrix-dispatcher.util';
|
||||
import { type 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';
|
||||
@@ -12,24 +13,35 @@ import { getWorkspaceMigrationV2CreateIndexAction } from 'src/engine/workspace-m
|
||||
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';
|
||||
export type WorkspaceMigrationBuildArgs = {
|
||||
workspaceId: string;
|
||||
inferDeletionFromMissingObjectFieldIndex?: boolean;
|
||||
} & FromTo<FlatObjectMetadataMaps, 'FlatObjectMetadataMaps'>;
|
||||
@Injectable()
|
||||
export class WorkspaceMigrationBuilderV2Service {
|
||||
constructor() {}
|
||||
|
||||
build({
|
||||
objectMetadataFromToInputs,
|
||||
fromFlatObjectMetadataMaps,
|
||||
toFlatObjectMetadataMaps,
|
||||
workspaceId,
|
||||
inferDeletionFromMissingObjectFieldIndex = true,
|
||||
}: {
|
||||
objectMetadataFromToInputs: FromTo<FlatObjectMetadata[]>;
|
||||
workspaceId: string;
|
||||
inferDeletionFromMissingObjectFieldIndex?: boolean;
|
||||
}): WorkspaceMigrationV2 {
|
||||
}: WorkspaceMigrationBuildArgs): WorkspaceMigrationV2 {
|
||||
const fromFlatObjectMetadatas =
|
||||
fromFlatObjectMetadataMapsToFlatObjectMetadatas(
|
||||
fromFlatObjectMetadataMaps,
|
||||
);
|
||||
const toFlatObjectMetadatas =
|
||||
fromFlatObjectMetadataMapsToFlatObjectMetadatas(toFlatObjectMetadataMaps);
|
||||
|
||||
const {
|
||||
created: createdObjectMetadata,
|
||||
deleted: deletedObjectMetadata,
|
||||
updated: updatedObjectMetadata,
|
||||
} = deletedCreatedUpdatedMatrixDispatcher(objectMetadataFromToInputs);
|
||||
} = deletedCreatedUpdatedMatrixDispatcher({
|
||||
from: fromFlatObjectMetadatas,
|
||||
to: toFlatObjectMetadatas,
|
||||
});
|
||||
|
||||
const objectWorkspaceMigrationActions =
|
||||
buildWorkspaceMigrationV2ObjectActions({
|
||||
|
||||
+8
-3
@@ -3,6 +3,7 @@ import { InjectDataSource } from '@nestjs/typeorm';
|
||||
|
||||
import { DataSource } from 'typeorm';
|
||||
|
||||
import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
import { WorkspaceMetadataCacheService } from 'src/engine/metadata-modules/workspace-metadata-cache/services/workspace-metadata-cache.service';
|
||||
import { WorkspaceMetadataVersionService } from 'src/engine/metadata-modules/workspace-metadata-version/services/workspace-metadata-version.service';
|
||||
import { WorkspacePermissionsCacheService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service';
|
||||
@@ -23,10 +24,12 @@ export class WorkspaceMigrationRunnerV2Service {
|
||||
private readonly coreDataSource: DataSource,
|
||||
) {}
|
||||
|
||||
run = async (workspaceMigration: WorkspaceMigrationV2) => {
|
||||
run = async (
|
||||
workspaceMigration: WorkspaceMigrationV2,
|
||||
): Promise<FlatObjectMetadataMaps> => {
|
||||
const queryRunner = this.coreDataSource.createQueryRunner();
|
||||
|
||||
const { flatObjectMetadataMaps } =
|
||||
const { flatObjectMetadataMaps: existingFlatObjectMetadataMaps } =
|
||||
await this.workspaceMetadataCacheService.getExistingOrRecomputeFlatObjectMetadataMaps(
|
||||
{
|
||||
workspaceId: workspaceMigration.workspaceId,
|
||||
@@ -36,7 +39,7 @@ export class WorkspaceMigrationRunnerV2Service {
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
let optimisticFlatObjectMetadataMaps = structuredClone(
|
||||
flatObjectMetadataMaps,
|
||||
existingFlatObjectMetadataMaps,
|
||||
);
|
||||
|
||||
try {
|
||||
@@ -73,6 +76,8 @@ export class WorkspaceMigrationRunnerV2Service {
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
return optimisticFlatObjectMetadataMaps;
|
||||
} catch (error) {
|
||||
if (queryRunner.isTransactionActive) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
|
||||
Reference in New Issue
Block a user