Label identifier validation v2 (#14867)
# Introduction Adding a hacky way to validate object against fields before fields validation ( bi-directional validation process ) If you encounter an identical setup we will add a specific devXp as cleanup validation but for the moment this seems enough close https://github.com/twentyhq/core-team-issues/issues/1639
This commit is contained in:
+33
-33
@@ -9,7 +9,7 @@ import { type FlatObjectMetadataEntityJsonbProperties } from 'src/engine/metadat
|
||||
import { type FlatObjectMetadataPropertiesToCompare } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata-properties-to-compare.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { isStandardMetadata } from 'src/engine/metadata-modules/utils/is-standard-metadata.util';
|
||||
import { type UpdateObjectAction } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-object-action-v2';
|
||||
import { type FlatObjectPropertiesUpdates } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-object-action-v2';
|
||||
import { transformMetadataForComparison } from 'src/engine/workspace-manager/workspace-sync-metadata/comparators/utils/transform-metadata-for-comparison.util';
|
||||
|
||||
export const compareTwoFlatObjectMetadata = ({
|
||||
@@ -50,42 +50,42 @@ export const compareTwoFlatObjectMetadata = ({
|
||||
);
|
||||
const objectMetadataDifference = diff(fromCompare, omit(toCompare, 'fields'));
|
||||
|
||||
return objectMetadataDifference.flatMap<
|
||||
UpdateObjectAction['updates'][number]
|
||||
>((difference) => {
|
||||
switch (difference.type) {
|
||||
case 'CHANGE': {
|
||||
const { oldValue, path, value } = difference;
|
||||
const property = path[0] as FlatObjectMetadataPropertiesToCompare;
|
||||
const isJsonb = FLAT_OBJECT_METADATA_JSONB_PROPERTIES.includes(
|
||||
property as FlatObjectMetadataEntityJsonbProperties,
|
||||
);
|
||||
return objectMetadataDifference.flatMap<FlatObjectPropertiesUpdates[number]>(
|
||||
(difference) => {
|
||||
switch (difference.type) {
|
||||
case 'CHANGE': {
|
||||
const { oldValue, path, value } = difference;
|
||||
const property = path[0] as FlatObjectMetadataPropertiesToCompare;
|
||||
const isJsonb = FLAT_OBJECT_METADATA_JSONB_PROPERTIES.includes(
|
||||
property as FlatObjectMetadataEntityJsonbProperties,
|
||||
);
|
||||
|
||||
if (isJsonb) {
|
||||
return {
|
||||
from: parseJson(oldValue),
|
||||
to: parseJson(value),
|
||||
property,
|
||||
};
|
||||
}
|
||||
|
||||
if (isJsonb) {
|
||||
return {
|
||||
from: parseJson(oldValue),
|
||||
to: parseJson(value),
|
||||
from: oldValue,
|
||||
to: value,
|
||||
property,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
from: oldValue,
|
||||
to: value,
|
||||
property,
|
||||
};
|
||||
case 'CREATE':
|
||||
case 'REMOVE': {
|
||||
// Should never occurs ? should throw ?
|
||||
return [];
|
||||
}
|
||||
default: {
|
||||
assertUnreachable(
|
||||
difference,
|
||||
`Unexpected difference type: ${difference['type']}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
case 'CREATE':
|
||||
case 'REMOVE': {
|
||||
// Should never occurs ? should throw ?
|
||||
return [];
|
||||
}
|
||||
default: {
|
||||
assertUnreachable(
|
||||
difference,
|
||||
`Unexpected difference type: ${difference['type']}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
+5
-6
@@ -6,7 +6,7 @@ import {
|
||||
import { type AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { findObjectFieldsInFlatFieldMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-object-fields-in-flat-field-metadata-maps-or-throw.util';
|
||||
import { findObjectFlatFieldMetadatasOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-object-fields-in-flat-field-metadata-maps-or-throw.util';
|
||||
import { findRelationFlatFieldMetadataTargetFlatFieldMetadataOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-relation-flat-field-metadatas-target-flat-field-metadata-or-throw.util';
|
||||
import { isMorphOrRelationFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-metadata.util';
|
||||
import { type FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type';
|
||||
@@ -52,11 +52,10 @@ export const fromDeleteObjectInputToFlatFieldMetadatasToDelete = ({
|
||||
);
|
||||
}
|
||||
|
||||
const { objectFlatFieldMetadatas } =
|
||||
findObjectFieldsInFlatFieldMetadataMapsOrThrow({
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata: flatObjectMetadataToDelete,
|
||||
});
|
||||
const { objectFlatFieldMetadatas } = findObjectFlatFieldMetadatasOrThrow({
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata: flatObjectMetadataToDelete,
|
||||
});
|
||||
const flatFieldMetadatasToDelete = objectFlatFieldMetadatas.flatMap(
|
||||
(flatFieldMetadata) => {
|
||||
if (isMorphOrRelationFlatFieldMetadata(flatFieldMetadata)) {
|
||||
|
||||
+5
-6
@@ -1,7 +1,7 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
|
||||
import { findObjectFieldsInFlatFieldMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-object-fields-in-flat-field-metadata-maps-or-throw.util';
|
||||
import { findObjectFlatFieldMetadatasOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-object-fields-in-flat-field-metadata-maps-or-throw.util';
|
||||
import { type FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { generateFlatIndexMetadataWithNameOrThrow } from 'src/engine/metadata-modules/index-metadata/utils/generate-flat-index.util';
|
||||
@@ -31,11 +31,10 @@ export const recomputeIndexAfterFlatObjectMetadataSingularNameUpdate = ({
|
||||
nameSingular: updatedSingularName,
|
||||
};
|
||||
|
||||
const { objectFlatFieldMetadatas } =
|
||||
findObjectFieldsInFlatFieldMetadataMapsOrThrow({
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata: optimisticFlatObjectMetadata,
|
||||
});
|
||||
const { objectFlatFieldMetadatas } = findObjectFlatFieldMetadatasOrThrow({
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata: optimisticFlatObjectMetadata,
|
||||
});
|
||||
|
||||
return allRelatedFlatIndexMetadata.map<FlatIndexMetadata>((flatIndex) => {
|
||||
const newIndex = generateFlatIndexMetadataWithNameOrThrow({
|
||||
|
||||
+5
-6
@@ -8,7 +8,7 @@ import { type AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all
|
||||
import { computeMorphOrRelationFieldJoinColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-morph-or-relation-field-join-column-name.util';
|
||||
import { computeMorphRelationFieldName } from 'src/engine/metadata-modules/field-metadata/utils/compute-morph-relation-field-name.util';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { findObjectFieldsInFlatFieldMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-object-fields-in-flat-field-metadata-maps-or-throw.util';
|
||||
import { findObjectFlatFieldMetadatasOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-object-fields-in-flat-field-metadata-maps-or-throw.util';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { getFlatObjectMetadataTargetMorphRelationFlatFieldMetadatasOrThrow } from 'src/engine/metadata-modules/flat-object-metadata/utils/get-flat-object-metadata-many-to-one-target-morph-relation-flat-field-metadatas-or-throw.util';
|
||||
|
||||
@@ -43,11 +43,10 @@ export const renameRelatedMorphFieldOnObjectNamesUpdate = ({
|
||||
toFlatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
}: RenameRelatedMorphFieldOnObjectNamesUpdateArgs): FlatFieldMetadata<FieldMetadataType.MORPH_RELATION>[] => {
|
||||
const { objectFlatFieldMetadatas } =
|
||||
findObjectFieldsInFlatFieldMetadataMapsOrThrow({
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata: fromFlatObjectMetadata,
|
||||
});
|
||||
const { objectFlatFieldMetadatas } = findObjectFlatFieldMetadatasOrThrow({
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata: fromFlatObjectMetadata,
|
||||
});
|
||||
const manyToOneMorphRelationFlatFieldMetadatas =
|
||||
getFlatObjectMetadataTargetMorphRelationFlatFieldMetadatasOrThrow({
|
||||
flatFieldMetadataMaps,
|
||||
|
||||
+13
-10
@@ -4,6 +4,8 @@ import {
|
||||
isLabelIdentifierFieldMetadataTypes,
|
||||
} from 'twenty-shared/utils';
|
||||
|
||||
import { type FlatEntityMaps } from 'src/engine/core-modules/common/types/flat-entity-maps.type';
|
||||
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/core-modules/common/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { type FlatObjectMetadataValidationError } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata-validation-error.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
@@ -11,24 +13,25 @@ import { ObjectMetadataExceptionCode } from 'src/engine/metadata-modules/object-
|
||||
|
||||
export const validateFlatObjectMetadataIdentifiers = ({
|
||||
flatObjectMetadata,
|
||||
objectFlatFieldMetadatas,
|
||||
flatFieldMetadataMaps,
|
||||
}: {
|
||||
flatObjectMetadata: Pick<
|
||||
FlatObjectMetadata,
|
||||
'labelIdentifierFieldMetadataId' | 'imageIdentifierFieldMetadataId'
|
||||
>;
|
||||
objectFlatFieldMetadatas: FlatFieldMetadata[];
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
}) => {
|
||||
const errors: FlatObjectMetadataValidationError[] = [];
|
||||
|
||||
const { labelIdentifierFieldMetadataId, imageIdentifierFieldMetadataId } =
|
||||
flatObjectMetadata;
|
||||
|
||||
// TODO should not be nullable
|
||||
if (isDefined(labelIdentifierFieldMetadataId)) {
|
||||
const flatFieldMetadata = objectFlatFieldMetadatas.find(
|
||||
(flatFieldMetadata) =>
|
||||
flatFieldMetadata.id === labelIdentifierFieldMetadataId,
|
||||
);
|
||||
const flatFieldMetadata = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: labelIdentifierFieldMetadataId,
|
||||
flatEntityMaps: flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(flatFieldMetadata)) {
|
||||
errors.push({
|
||||
@@ -48,10 +51,10 @@ export const validateFlatObjectMetadataIdentifiers = ({
|
||||
}
|
||||
|
||||
if (isDefined(imageIdentifierFieldMetadataId)) {
|
||||
const relatedFlatFieldMetadata = objectFlatFieldMetadatas.find(
|
||||
(flatFieldMetadata) =>
|
||||
flatFieldMetadata.id === imageIdentifierFieldMetadataId,
|
||||
);
|
||||
const relatedFlatFieldMetadata = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: imageIdentifierFieldMetadataId,
|
||||
flatEntityMaps: flatFieldMetadataMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(relatedFlatFieldMetadata)) {
|
||||
errors.push({
|
||||
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
|
||||
import { type FlatEntityMaps } from 'src/engine/core-modules/common/types/flat-entity-maps.type';
|
||||
import { type FlatObjectMetadataValidationError } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata-validation-error.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { areFlatObjectMetadataNamesSyncedWithLabels } from 'src/engine/metadata-modules/flat-object-metadata/utils/are-flat-object-metadata-names-synced-with-labels.util';
|
||||
import { validateFlatObjectMetadataLabel } from 'src/engine/metadata-modules/flat-object-metadata/validators/utils/validate-flat-object-metadata-label.util';
|
||||
import { validateFlatObjectMetadataNames } from 'src/engine/metadata-modules/flat-object-metadata/validators/utils/validate-flat-object-metadata-name.util';
|
||||
import { ObjectMetadataExceptionCode } from 'src/engine/metadata-modules/object-metadata/object-metadata.exception';
|
||||
import { doesOtherObjectWithSameNameExists } from 'src/engine/metadata-modules/utils/validate-no-other-object-with-same-name-exists-or-throw.util';
|
||||
|
||||
export const validateFlatObjectMetadataNameAndLabels = ({
|
||||
optimisticFlatObjectMetadataMaps,
|
||||
flatObjectMetadataToValidate,
|
||||
}: {
|
||||
flatObjectMetadataToValidate: FlatObjectMetadata;
|
||||
optimisticFlatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>;
|
||||
}): FlatObjectMetadataValidationError[] => {
|
||||
const errors: FlatObjectMetadataValidationError[] = [];
|
||||
|
||||
errors.push(
|
||||
...validateFlatObjectMetadataNames({
|
||||
namePlural: flatObjectMetadataToValidate.namePlural,
|
||||
nameSingular: flatObjectMetadataToValidate.nameSingular,
|
||||
}),
|
||||
);
|
||||
|
||||
errors.push(
|
||||
...validateFlatObjectMetadataLabel({
|
||||
labelPlural: flatObjectMetadataToValidate.labelPlural,
|
||||
labelSingular: flatObjectMetadataToValidate.labelSingular,
|
||||
}),
|
||||
);
|
||||
|
||||
if (
|
||||
flatObjectMetadataToValidate.isLabelSyncedWithName &&
|
||||
!areFlatObjectMetadataNamesSyncedWithLabels(flatObjectMetadataToValidate)
|
||||
) {
|
||||
errors.push({
|
||||
code: ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
|
||||
message: t`Names are not synced with labels`,
|
||||
userFriendlyMessage: t`Names are not synced with labels`,
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
doesOtherObjectWithSameNameExists({
|
||||
objectMetadataNamePlural: flatObjectMetadataToValidate.namePlural,
|
||||
objectMetadataNameSingular: flatObjectMetadataToValidate.nameSingular,
|
||||
objectMetadataMaps: optimisticFlatObjectMetadataMaps,
|
||||
existingObjectMetadataId: flatObjectMetadataToValidate.id,
|
||||
})
|
||||
) {
|
||||
errors.push({
|
||||
code: ObjectMetadataExceptionCode.OBJECT_ALREADY_EXISTS,
|
||||
message: 'Object already exists',
|
||||
userFriendlyMessage: t`Object already exists`,
|
||||
});
|
||||
}
|
||||
|
||||
return errors;
|
||||
};
|
||||
Reference in New Issue
Block a user