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 11:30:13 +02:00
committed by GitHub
parent 810fe06aa3
commit d8ec2e3959
9 changed files with 239 additions and 148 deletions
@@ -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<