Field metadata v2 relation (#13562)

# Introduction
- deferrable constraint on foreignKey definition
- fix relation creation input transpilation to flat field metadata
- fix validation to allow contextual validation on not already created
field metadata ( usefull for relation, and will be necessary for the
import )
This commit is contained in:
Paul Rastoin
2025-08-04 09:30:13 +00:00
committed by GitHub
parent 810fe06aa3
commit d8ec2e3959
9 changed files with 239 additions and 148 deletions
@@ -0,0 +1,31 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class MakeFieldMetadataRelationConstraintDeferrable1752993324991
implements MigrationInterface
{
name = 'MakeFieldMetadataRelationConstraintDeferrable1752993324991';
public async up(queryRunner: QueryRunner): Promise<void> {
// Drop the existing constraint
await queryRunner.query(
`ALTER TABLE "core"."fieldMetadata" DROP CONSTRAINT "FK_47a6c57e1652b6475f8248cff78"`,
);
// Recreate it as deferrable and initially deferred
await queryRunner.query(
`ALTER TABLE "core"."fieldMetadata" ADD CONSTRAINT "FK_47a6c57e1652b6475f8248cff78" FOREIGN KEY ("relationTargetFieldMetadataId") REFERENCES "core"."fieldMetadata"("id") DEFERRABLE INITIALLY DEFERRED`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
// Drop the deferrable constraint
await queryRunner.query(
`ALTER TABLE "core"."fieldMetadata" DROP CONSTRAINT "FK_47a6c57e1652b6475f8248cff78"`,
);
// Recreate it as non-deferrable
await queryRunner.query(
`ALTER TABLE "core"."fieldMetadata" ADD CONSTRAINT "FK_47a6c57e1652b6475f8248cff78" FOREIGN KEY ("relationTargetFieldMetadataId") REFERENCES "core"."fieldMetadata"("id")`,
);
}
}
@@ -49,13 +49,13 @@ export type FieldMetadataAddressSettings = {
};
type FieldMetadataSettingsMapping = {
[FieldMetadataType.NUMBER]: FieldMetadataNumberSettings;
[FieldMetadataType.DATE]: FieldMetadataDateSettings;
[FieldMetadataType.DATE_TIME]: FieldMetadataDateTimeSettings;
[FieldMetadataType.TEXT]: FieldMetadataTextSettings;
[FieldMetadataType.NUMBER]: FieldMetadataNumberSettings | null;
[FieldMetadataType.DATE]: FieldMetadataDateSettings | null;
[FieldMetadataType.DATE_TIME]: FieldMetadataDateTimeSettings | null;
[FieldMetadataType.TEXT]: FieldMetadataTextSettings | null;
[FieldMetadataType.RELATION]: FieldMetadataRelationSettings;
[FieldMetadataType.MORPH_RELATION]: FieldMetadataRelationSettings;
[FieldMetadataType.ADDRESS]: FieldMetadataAddressSettings;
[FieldMetadataType.ADDRESS]: FieldMetadataAddressSettings | null;
[FieldMetadataType.MORPH_RELATION]: FieldMetadataRelationSettings | null; // TODO Should not be null
};
export type AllFieldMetadataSettings =
@@ -67,5 +67,5 @@ export type FieldMetadataSettings<
IsExactly<T, FieldMetadataType> extends true
? null | AllFieldMetadataSettings // Could be improved to be | unknown
: T extends keyof FieldMetadataSettingsMapping
? FieldMetadataSettingsMapping[T] | null
? FieldMetadataSettingsMapping[T]
: never | null;
@@ -13,12 +13,10 @@ import {
FieldMetadataExceptionCode,
} from 'src/engine/metadata-modules/field-metadata/field-metadata.exception';
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 { fromCreateFieldInputToFlatFieldAndItsFlatObjectMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/from-create-field-input-to-flat-field-and-its-flat-object-metadata.util';
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
import { dispatchAndMergeFlatFieldMetadatasInFlatObjectMetadatas } from 'src/engine/metadata-modules/flat-object-metadata/utils/dispatch-and-merge-flat-field-metadatas-in-flat-object-metadatas.util';
import { fromObjectMetadataMapsToFlatObjectMetadatas } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-object-metadata-maps-to-flat-object-metadatas.util';
import { mergeTwoFlatObjectMetadatas } from 'src/engine/metadata-modules/flat-object-metadata/utils/merge-two-flat-object-metadatas.util';
import { getFieldMetadataEntityFromCachedObjectMetadataMaps } from 'src/engine/metadata-modules/utils/get-field-metadata-entity-from-cached-object-metadata-maps.util';
import { removeFlatFieldMetadataFromFlatObjectMetadatas } from 'src/engine/metadata-modules/flat-object-metadata/utils/remove-flat-field-metadata-from-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';
@@ -68,67 +66,54 @@ export class FieldMetadataServiceV2 extends TypeOrmQueryService<FieldMetadataEnt
const existingFlatObjectMetadatas =
fromObjectMetadataMapsToFlatObjectMetadatas(objectMetadataMaps);
const flatFieldToCreateAndItsFlatObjectMetadataArray = await Promise.all(
fieldMetadataInputs.map(
async (fieldMetadataInput) =>
await fromCreateFieldInputToFlatFieldAndItsFlatObjectMetadata({
existingFlatObjectMetadatas,
rawCreateFieldInput: fieldMetadataInput,
}),
),
const flatFieldMetadatasToCreate = (
await Promise.all(
fieldMetadataInputs.map(
async (fieldMetadataInput) =>
await fromCreateFieldInputToFlatFieldAndItsFlatObjectMetadata({
existingFlatObjectMetadatas,
rawCreateFieldInput: fieldMetadataInput,
}),
),
)
).flat();
const optimisticRenderedFlatObjectMetadatas =
dispatchAndMergeFlatFieldMetadatasInFlatObjectMetadatas({
flatObjectMetadatas: existingFlatObjectMetadatas,
flatFieldMetadatas: flatFieldMetadatasToCreate,
});
const flatFieldMetadataValidationPromises = flatFieldMetadatasToCreate.map(
(flatFieldMetadataToValidate) =>
this.flatFieldMetadataValidatorService.validateOneFlatFieldMetadata({
existingFlatObjectMetadatas:
removeFlatFieldMetadataFromFlatObjectMetadatas({
flatFieldMetadata: flatFieldMetadataToValidate,
flatObjectMetadatas: optimisticRenderedFlatObjectMetadatas,
}),
flatFieldMetadataToValidate,
workspaceId,
}),
);
let flatObjectMetadatasWithNewFields: FlatObjectMetadata[] = [];
const createdFlatFieldMetadataValidationResult = (
await Promise.all(flatFieldMetadataValidationPromises)
)
.flat()
.filter(isDefined);
for (const flatFieldMetadataAndParentPair of flatFieldToCreateAndItsFlatObjectMetadataArray) {
const flatFieldMetadataValidationPromises =
flatFieldMetadataAndParentPair.flatMap<
Promise<FailedFlatFieldMetadataValidationExceptions[]>
>(({ flatFieldMetadata: flatFieldMetadataToValidate }) =>
this.flatFieldMetadataValidatorService.validateOneFlatFieldMetadata({
existingFlatObjectMetadatas,
flatFieldMetadataToValidate,
workspaceId,
}),
);
const createdFlatFieldMetadataValidationResult = (
await Promise.all(flatFieldMetadataValidationPromises)
)
.flat()
.filter(isDefined);
if (createdFlatFieldMetadataValidationResult.length > 0) {
const errors = createdFlatFieldMetadataValidationResult.flat();
throw new MultipleMetadataValidationErrors(
errors,
'Multiple validation errors occurred while creating field',
);
}
const updatedFlatObjectMetadatas =
flatFieldMetadataAndParentPair.map<FlatObjectMetadata>(
({ flatFieldMetadata, parentFlatObjectMetadata }) => {
return {
...parentFlatObjectMetadata,
flatFieldMetadatas: [
...parentFlatObjectMetadata.flatFieldMetadatas,
flatFieldMetadata,
],
};
},
);
flatObjectMetadatasWithNewFields = mergeTwoFlatObjectMetadatas({
destFlatObjectMetadatas: flatObjectMetadatasWithNewFields,
toMergeFlatObjectMetadatas: updatedFlatObjectMetadatas,
});
if (createdFlatFieldMetadataValidationResult.length > 0) {
throw new MultipleMetadataValidationErrors(
createdFlatFieldMetadataValidationResult,
'Multiple validation errors occurred while creating field',
);
}
const workspaceMigration = this.workspaceMigrationBuilderV2.build({
objectMetadataFromToInputs: {
from: existingFlatObjectMetadatas,
to: flatObjectMetadatasWithNewFields,
to: optimisticRenderedFlatObjectMetadatas,
},
inferDeletionFromMissingObjectFieldIndex: false,
workspaceId,
@@ -136,27 +121,7 @@ export class FieldMetadataServiceV2 extends TypeOrmQueryService<FieldMetadataEnt
await this.workspaceMigrationRunnerV2Service.run(workspaceMigration);
const recomputedCache =
await this.workspaceMetadataCacheService.getExistingOrRecomputeMetadataMaps(
{ workspaceId },
);
return flatFieldToCreateAndItsFlatObjectMetadataArray.flatMap<FieldMetadataEntity>(
(createdFlatFieldAndItsParentFlatObject) => {
return createdFlatFieldAndItsParentFlatObject
.map(
({
flatFieldMetadata: { id: fieldMetadataId },
parentFlatObjectMetadata: { id: objectMetadataId },
}) =>
getFieldMetadataEntityFromCachedObjectMetadataMaps({
fieldMetadataId,
objectMetadataId,
objectMetadataMaps: recomputedCache.objectMetadataMaps,
}),
)
.filter(isDefined);
},
);
// TODO refactor once the runner has been refactored to return created entities
return [];
}
}
@@ -165,7 +165,7 @@ type SettingsAssertions = [
Expect<
HasAllProperties<
RelationFieldMetadata,
{ settings: FieldMetadataRelationSettings | null }
{ settings: FieldMetadataRelationSettings }
>
>,
Expect<
@@ -12,6 +12,7 @@ import { FailedFlatFieldMetadataValidationExceptions } from 'src/engine/metadata
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
import { validateFlatFieldMetadataNameAvailability } from 'src/engine/metadata-modules/flat-field-metadata/validators/validate-flat-field-metadata-name-availability.validator';
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
import { mergeTwoFlatObjectMetadatas } from 'src/engine/metadata-modules/flat-object-metadata/utils/merge-two-flat-object-metadatas.util';
import {
ObjectMetadataException,
ObjectMetadataExceptionCode,
@@ -49,14 +50,18 @@ export class FlatFieldMetadataValidatorService {
FailedFlatFieldMetadataValidationExceptions[]
> {
const errors: FailedFlatFieldMetadataValidationExceptions[] = [];
const allFlatObjectMetadata = [
...existingFlatObjectMetadatas,
...(othersFlatObjectMetadataToValidate ?? []),
];
const allFlatObjectMetadata = isDefined(othersFlatObjectMetadataToValidate)
? mergeTwoFlatObjectMetadatas({
destFlatObjectMetadatas: existingFlatObjectMetadatas,
toMergeFlatObjectMetadatas: othersFlatObjectMetadataToValidate,
})
: existingFlatObjectMetadatas;
const parentFlatObjectMetadata = allFlatObjectMetadata.find(
(existingFlatObjectMetadata) =>
existingFlatObjectMetadata.id ===
flatFieldMetadataToValidate.objectMetadataId, // Question: Should we comparing unique identifier here ?
flatFieldMetadataToValidate.objectMetadataId,
);
if (!isDefined(parentFlatObjectMetadata)) {
@@ -24,19 +24,11 @@ type FromCreateFieldInputToFlatObjectMetadata = {
rawCreateFieldInput: CreateFieldInput;
existingFlatObjectMetadatas: FlatObjectMetadata[];
};
export type FlatFieldAndItsFlatObjectMetadata<
T extends FieldMetadataType = FieldMetadataType,
> = {
flatFieldMetadata: FlatFieldMetadata<T>;
parentFlatObjectMetadata: FlatObjectMetadata;
};
export const fromCreateFieldInputToFlatFieldAndItsFlatObjectMetadata = async ({
existingFlatObjectMetadatas,
rawCreateFieldInput,
}: FromCreateFieldInputToFlatObjectMetadata): Promise<
FlatFieldAndItsFlatObjectMetadata[]
> => {
}: FromCreateFieldInputToFlatObjectMetadata): Promise<FlatFieldMetadata[]> => {
if (rawCreateFieldInput.isRemoteCreation) {
throw new FieldMetadataException(
"Remote fields aren't supported",
@@ -86,15 +78,12 @@ export const fromCreateFieldInputToFlatFieldAndItsFlatObjectMetadata = async ({
case FieldMetadataType.RATING: {
return [
{
flatFieldMetadata: {
...commonFlatFieldMetadata,
type: createFieldInput.type,
settings: null,
defaultValue: commonFlatFieldMetadata.defaultValue as string, // Could this be improved ?
options: generateRatingOptions(),
} satisfies FlatFieldMetadata<typeof createFieldInput.type>,
parentFlatObjectMetadata,
},
...commonFlatFieldMetadata,
type: createFieldInput.type,
settings: null,
defaultValue: commonFlatFieldMetadata.defaultValue as string, // Could this be improved ?
options: generateRatingOptions(),
} satisfies FlatFieldMetadata<typeof createFieldInput.type>,
];
}
case FieldMetadataType.SELECT:
@@ -110,15 +99,12 @@ export const fromCreateFieldInputToFlatFieldAndItsFlatObjectMetadata = async ({
return [
{
flatFieldMetadata: {
...commonFlatFieldMetadata,
type: createFieldInput.type,
options,
defaultValue: commonFlatFieldMetadata.defaultValue as string, // Could this be improved ?
settings: null,
} satisfies FlatFieldMetadata<typeof createFieldInput.type>,
parentFlatObjectMetadata,
},
...commonFlatFieldMetadata,
type: createFieldInput.type,
options,
defaultValue: commonFlatFieldMetadata.defaultValue as string, // Could this be improved ?
settings: null,
} satisfies FlatFieldMetadata<typeof createFieldInput.type>,
];
}
case FieldMetadataType.UUID:
@@ -143,11 +129,8 @@ export const fromCreateFieldInputToFlatFieldAndItsFlatObjectMetadata = async ({
case FieldMetadataType.TS_VECTOR: {
return [
{
flatFieldMetadata: {
...commonFlatFieldMetadata,
type: createFieldInput.type,
},
parentFlatObjectMetadata,
...commonFlatFieldMetadata,
type: createFieldInput.type,
},
];
}
@@ -2,6 +2,8 @@ import { FieldMetadataType } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { v4 } from 'uuid';
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
import { CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input';
import {
FieldMetadataException,
@@ -9,11 +11,32 @@ import {
} from 'src/engine/metadata-modules/field-metadata/field-metadata.exception';
import { validateRelationCreationPayloadOrThrow } from 'src/engine/metadata-modules/field-metadata/utils/validate-relation-creation-payload.util';
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
import { FlatFieldAndItsFlatObjectMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/from-create-field-input-to-flat-field-and-its-flat-object-metadata.util';
import { getDefaultFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/get-default-flat-field-metadata-from-create-field-input.util';
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
import { RelationOnDeleteAction } from 'src/engine/metadata-modules/relation-metadata/relation-on-delete-action.type';
import { computeMetadataNameFromLabel } from 'src/engine/metadata-modules/utils/validate-name-and-label-are-sync-or-throw.util';
type ComputeFieldMetadataRelationSettingsForRelationTypeArgs = {
relationType: RelationType;
fieldMetadataName: string;
};
const computeFieldMetadataRelationSettingsForRelationType = ({
fieldMetadataName,
relationType,
}: ComputeFieldMetadataRelationSettingsForRelationTypeArgs) => {
if (relationType === RelationType.MANY_TO_ONE) {
return {
relationType: RelationType.MANY_TO_ONE,
onDelete: RelationOnDeleteAction.SET_NULL,
joinColumnName: fieldMetadataName,
};
}
return {
relationType: RelationType.ONE_TO_MANY,
};
};
type FromRelationCreateFieldInputToFlatFieldMetadataArgs = {
createFieldInput: CreateFieldInput;
existingFlatObjectMetadatas: FlatObjectMetadata[];
@@ -24,7 +47,7 @@ export const fromRelationCreateFieldInputToFlatFieldMetadata = async ({
sourceParentFlatObjectMetadata,
createFieldInput,
}: FromRelationCreateFieldInputToFlatFieldMetadataArgs): Promise<
FlatFieldAndItsFlatObjectMetadata[]
FlatFieldMetadata[]
> => {
const { relationCreationPayload } = createFieldInput;
@@ -48,6 +71,11 @@ export const fromRelationCreateFieldInputToFlatFieldMetadata = async ({
);
}
const sourceFlatFieldMetadataSettings =
computeFieldMetadataRelationSettingsForRelationType({
fieldMetadataName: createFieldInput.name,
relationType: relationCreationPayload.type,
});
const targetRelationTargetFieldMetadataId = v4();
const sourceRelationTargetFieldMetadataId = v4();
const sourceFlatFieldMetadata: Omit<
@@ -58,33 +86,43 @@ export const fromRelationCreateFieldInputToFlatFieldMetadata = async ({
createFieldInput,
fieldMetadataId: sourceRelationTargetFieldMetadataId,
}),
icon: createFieldInput.icon ?? 'IconRelationOneToMany',
type: FieldMetadataType.RELATION,
defaultValue: null,
settings: null,
settings: sourceFlatFieldMetadataSettings,
options: null,
relationTargetFieldMetadataId: targetRelationTargetFieldMetadataId, // Note: this won't work until we enable deferred transaction
relationTargetObjectMetadataId: targetParentFlatObjectMetadata.id,
flatRelationTargetObjectMetadata: targetParentFlatObjectMetadata,
};
const targetCreateFieldInput: CreateFieldInput = {
icon: relationCreationPayload.targetFieldIcon ?? 'Icon123',
label: relationCreationPayload.targetFieldLabel,
name: `${computeMetadataNameFromLabel(
relationCreationPayload.targetFieldLabel,
)}Id`,
objectMetadataId: targetParentFlatObjectMetadata.id,
type: FieldMetadataType.RELATION,
workspaceId: createFieldInput.workspaceId,
};
const targetFlatFieldMetadataSettings =
computeFieldMetadataRelationSettingsForRelationType({
fieldMetadataName: targetCreateFieldInput.name,
relationType:
relationCreationPayload.type === RelationType.ONE_TO_MANY
? RelationType.MANY_TO_ONE
: RelationType.ONE_TO_MANY,
});
const targetFlatFieldMetadata: FlatFieldMetadata<FieldMetadataType.RELATION> =
{
...getDefaultFlatFieldMetadata({
createFieldInput: {
icon: relationCreationPayload.targetFieldIcon,
label: relationCreationPayload.targetFieldLabel,
name: `${computeMetadataNameFromLabel(
relationCreationPayload.targetFieldLabel,
)}Id`,
objectMetadataId: targetParentFlatObjectMetadata.id,
type: FieldMetadataType.RELATION,
workspaceId: createFieldInput.workspaceId,
},
createFieldInput: targetCreateFieldInput,
fieldMetadataId: targetRelationTargetFieldMetadataId,
}),
type: FieldMetadataType.RELATION,
defaultValue: null,
settings: null,
settings: targetFlatFieldMetadataSettings,
options: null,
relationTargetFieldMetadataId: sourceRelationTargetFieldMetadataId,
relationTargetObjectMetadataId: sourceParentFlatObjectMetadata.id,
@@ -94,15 +132,9 @@ export const fromRelationCreateFieldInputToFlatFieldMetadata = async ({
return [
{
flatFieldMetadata: {
...sourceFlatFieldMetadata,
flatRelationTargetFieldMetadata: targetFlatFieldMetadata,
},
parentFlatObjectMetadata: sourceParentFlatObjectMetadata,
...sourceFlatFieldMetadata,
flatRelationTargetFieldMetadata: targetFlatFieldMetadata,
},
{
flatFieldMetadata: targetFlatFieldMetadata,
parentFlatObjectMetadata: targetParentFlatObjectMetadata,
},
] satisfies FlatFieldAndItsFlatObjectMetadata<FieldMetadataType.RELATION>[];
targetFlatFieldMetadata,
] satisfies FlatFieldMetadata<FieldMetadataType.RELATION>[];
};
@@ -0,0 +1,49 @@
import { isDefined } from 'twenty-shared/utils';
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
import { mergeTwoFlatFieldMetadatas } from 'src/engine/metadata-modules/flat-field-metadata/utils/merge-two-flat-field-metadatas.util';
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
type DispatchAndMergeFlatFieldMetadatasInFlatObjectMetadatasArgs = {
flatObjectMetadatas: FlatObjectMetadata[];
flatFieldMetadatas: FlatFieldMetadata[];
};
export const dispatchAndMergeFlatFieldMetadatasInFlatObjectMetadatas = ({
flatFieldMetadatas,
flatObjectMetadatas,
}: DispatchAndMergeFlatFieldMetadatasInFlatObjectMetadatasArgs): FlatObjectMetadata[] => {
const initialAccumulator: Record<string, FlatFieldMetadata[]> = {};
const flatFieldMetadataGroupedByFlatObjectMetadataId =
flatFieldMetadatas.reduce((acc, flatFieldMetadata) => {
const { objectMetadataId } = flatFieldMetadata;
const occurrence = acc[objectMetadataId];
if (isDefined(occurrence)) {
return {
...acc,
[objectMetadataId]: [...occurrence, flatFieldMetadata],
};
}
return {
[flatFieldMetadata.objectMetadataId]: [flatFieldMetadata],
};
}, initialAccumulator);
return flatObjectMetadatas.map((flatObjectMetadata) => {
const toMergeFlatFieldMetadatas =
flatFieldMetadataGroupedByFlatObjectMetadataId[flatObjectMetadata.id];
if (!isDefined(toMergeFlatFieldMetadatas)) {
return flatObjectMetadata;
}
return {
...flatObjectMetadata,
flatFieldMetadatas: mergeTwoFlatFieldMetadatas({
destFlatFieldMetadatas: flatObjectMetadata.flatFieldMetadatas,
toMergeFlatFieldMetadatas,
}),
};
});
};
@@ -0,0 +1,26 @@
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
export const removeFlatFieldMetadataFromFlatObjectMetadatas = ({
flatFieldMetadata: flatFieldMetadataToRemove,
flatObjectMetadatas,
}: {
flatObjectMetadatas: FlatObjectMetadata[];
flatFieldMetadata: FlatFieldMetadata;
}) => {
return flatObjectMetadatas.map((flatObjectMetadata) => {
if (flatObjectMetadata.id !== flatFieldMetadataToRemove.objectMetadataId) {
return flatObjectMetadata;
}
const flatFieldMetadatas = flatObjectMetadata.flatFieldMetadatas.filter(
({ id: flatFieldMetadataId }) =>
flatFieldMetadataId !== flatFieldMetadataToRemove.id,
);
return {
...flatObjectMetadata,
flatFieldMetadatas,
};
});
};