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:
+31
@@ -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")`,
|
||||
);
|
||||
}
|
||||
}
|
||||
+7
-7
@@ -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;
|
||||
|
||||
+44
-79
@@ -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 [];
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -165,7 +165,7 @@ type SettingsAssertions = [
|
||||
Expect<
|
||||
HasAllProperties<
|
||||
RelationFieldMetadata,
|
||||
{ settings: FieldMetadataRelationSettings | null }
|
||||
{ settings: FieldMetadataRelationSettings }
|
||||
>
|
||||
>,
|
||||
Expect<
|
||||
|
||||
+10
-5
@@ -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)) {
|
||||
|
||||
+15
-32
@@ -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,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
+56
-24
@@ -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>[];
|
||||
};
|
||||
|
||||
+49
@@ -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,
|
||||
}),
|
||||
};
|
||||
});
|
||||
};
|
||||
+26
@@ -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,
|
||||
};
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user