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:
Paul Rastoin
2025-10-07 13:27:21 +02:00
committed by GitHub
parent 0670bb2043
commit 23e21cbeea
22 changed files with 417 additions and 233 deletions
@@ -18,7 +18,7 @@ import { RelationDTO } from 'src/engine/metadata-modules/field-metadata/dtos/rel
import { type FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { resolveFieldMetadataStandardOverride } from 'src/engine/metadata-modules/field-metadata/utils/resolve-field-metadata-standard-override.util';
import { findAllOthersMorphRelationFlatFieldMetadatasOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/find-all-others-morph-relation-flat-field-metadatas-or-throw.util';
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 { fromFlatFieldMetadataToFieldMetadataDto } from 'src/engine/metadata-modules/flat-field-metadata/utils/from-flat-field-metadata-to-field-metadata-dto.util';
import { fromMorphOrRelationFlatFieldMetadataToRelationDto } from 'src/engine/metadata-modules/flat-field-metadata/utils/from-morph-or-relation-flat-field-metadata-to-relation-dto.util';
import { isFlatFieldMetadataOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util';
@@ -339,7 +339,7 @@ export class DataloaderService {
flatEntityMaps: flatObjectMetadataMaps,
});
const { objectFlatFieldMetadatas } =
findObjectFieldsInFlatFieldMetadataMapsOrThrow({
findObjectFlatFieldMetadatasOrThrow({
flatFieldMetadataMaps: flatFieldMetadataMaps,
flatObjectMetadata,
});
@@ -7,7 +7,7 @@ import {
} from 'src/engine/core-modules/common/exceptions/flat-entity-maps.exception';
import { type FlatEntityMaps } from 'src/engine/core-modules/common/types/flat-entity-maps.type';
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 { isFlatFieldMetadataOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util';
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
@@ -30,11 +30,10 @@ export const findAllOthersMorphRelationFlatFieldMetadatasOrThrow = ({
);
}
const { objectFlatFieldMetadatas } =
findObjectFieldsInFlatFieldMetadataMapsOrThrow({
flatFieldMetadataMaps,
flatObjectMetadata,
});
const { objectFlatFieldMetadatas } = findObjectFlatFieldMetadatasOrThrow({
flatFieldMetadataMaps,
flatObjectMetadata,
});
return objectFlatFieldMetadatas.filter(
(
@@ -9,24 +9,22 @@ type FindObjectFieldsInFlatFieldMetadataMapsArgs = {
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
flatObjectMetadata: FlatObjectMetadata;
};
export const findObjectFieldsInFlatFieldMetadataMapsOrThrow = ({
export const findObjectFlatFieldMetadatasOrThrow = ({
flatFieldMetadataMaps,
flatObjectMetadata,
}: FindObjectFieldsInFlatFieldMetadataMapsArgs): {
objectFlatFieldMetadataById: FlatEntityMaps<FlatFieldMetadata>;
objectFlatFieldMetadatas: FlatFieldMetadata[];
} => {
const objectFlatFieldMetadataById = getSubFlatEntityMapsOrThrow({
const objectFlatFieldMetadataMaps = getSubFlatEntityMapsOrThrow({
flatEntityIds: flatObjectMetadata.fieldMetadataIds,
flatEntityMaps: flatFieldMetadataMaps,
});
const objectFlatFieldMetadatas = Object.values(
objectFlatFieldMetadataById.byId,
objectFlatFieldMetadataMaps.byId,
).filter(isDefined);
return {
objectFlatFieldMetadataById,
objectFlatFieldMetadatas,
};
};
@@ -13,7 +13,7 @@ import {
} 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 { computeFlatFieldMetadataRelatedFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/compute-flat-field-metadata-related-flat-field-metadata.util';
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 { generateFlatIndexMetadataWithNameOrThrow } from 'src/engine/metadata-modules/index-metadata/utils/generate-flat-index.util';
@@ -140,11 +140,10 @@ export const fromDeleteFieldInputToFlatFieldMetadatasToDelete = ({
flatEntityMaps: existingFlatObjectMetadataMaps,
flatEntityId: flatIndex.objectMetadataId,
});
const { objectFlatFieldMetadatas } =
findObjectFieldsInFlatFieldMetadataMapsOrThrow({
flatFieldMetadataMaps: existingFlatFieldMetadataMaps,
flatObjectMetadata,
});
const { objectFlatFieldMetadatas } = findObjectFlatFieldMetadatasOrThrow({
flatFieldMetadataMaps: existingFlatFieldMetadataMaps,
flatObjectMetadata,
});
const newIndex = generateFlatIndexMetadataWithNameOrThrow({
flatObjectMetadata,
@@ -2,7 +2,7 @@ import { isDefined } from 'twenty-shared/utils';
import { type AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
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 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';
@@ -35,11 +35,10 @@ export const recomputeIndexOnFlatFieldMetadataNameUpdate = ({
return [];
}
const { objectFlatFieldMetadatas } =
findObjectFieldsInFlatFieldMetadataMapsOrThrow({
flatFieldMetadataMaps,
flatObjectMetadata,
});
const { objectFlatFieldMetadatas } = findObjectFlatFieldMetadatasOrThrow({
flatFieldMetadataMaps,
flatObjectMetadata,
});
const optimisticObjectFlatFieldMetadatas = objectFlatFieldMetadatas.map(
(flatFieldMetadata) => {
if (flatFieldMetadata.id === fromFlatFieldMetadata.id) {
@@ -10,7 +10,7 @@ import { computeCompositeColumnName } from 'src/engine/metadata-modules/field-me
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
import { type FlatFieldMetadataValidationError } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata-validation-error.type';
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 { isFlatFieldMetadataOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util';
import { isMorphOrRelationFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-metadata.util';
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
@@ -50,11 +50,10 @@ export const validateFlatFieldMetadataNameAvailability = ({
remainingFlatEntityMapsToValidate?: FlatEntityMaps<FlatFieldMetadata>;
}): FlatFieldMetadataValidationError[] => {
const errors: FlatFieldMetadataValidationError[] = [];
const { objectFlatFieldMetadatas } =
findObjectFieldsInFlatFieldMetadataMapsOrThrow({
flatFieldMetadataMaps,
flatObjectMetadata,
});
const { objectFlatFieldMetadatas } = findObjectFlatFieldMetadatasOrThrow({
flatFieldMetadataMaps,
flatObjectMetadata,
});
const reservedCompositeFieldsNames = getReservedCompositeFieldNames(
objectFlatFieldMetadatas,
);
@@ -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']}`,
);
}
}
});
},
);
};
@@ -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)) {
@@ -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({
@@ -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,
@@ -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({
@@ -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;
};
@@ -14,7 +14,7 @@ import { FlatView } from 'src/engine/core-modules/view/flat-view/types/flat-view
import { fromCreateViewFieldInputToFlatViewFieldToCreate } from 'src/engine/core-modules/view/flat-view/utils/from-create-view-field-input-to-flat-view-field-to-create.util';
import { fromCreateViewInputToFlatViewToCreate } from 'src/engine/core-modules/view/flat-view/utils/from-create-view-input-to-flat-view-to-create.util';
import { 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 { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
import { fromCreateObjectInputToFlatObjectMetadataAndFlatFieldMetadatasToCreate } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-create-object-input-to-flat-object-metadata-and-flat-field-metadatas-to-create.util';
import { fromDeleteObjectInputToFlatFieldMetadatasToDelete } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-delete-object-input-to-flat-field-metadatas-to-delete.util';
@@ -353,11 +353,10 @@ export class ObjectMetadataServiceV2 {
flatEntityMaps: existingFlatViewMaps,
});
const { objectFlatFieldMetadatas } =
findObjectFieldsInFlatFieldMetadataMapsOrThrow({
flatFieldMetadataMaps: toFlatFieldMetadataMaps,
flatObjectMetadata: flatObjectMetadataToCreate,
});
const { objectFlatFieldMetadatas } = findObjectFlatFieldMetadatasOrThrow({
flatFieldMetadataMaps: toFlatFieldMetadataMaps,
flatObjectMetadata: flatObjectMetadataToCreate,
});
const flatDefaultViewFieldsToCreate =
await this.createDefaultFlatViewFields({
objectFlatFieldMetadatas,
@@ -12,13 +12,13 @@ import {
} from 'typeorm';
import { type WorkspaceEntityDuplicateCriteria } from 'src/engine/api/graphql/workspace-query-builder/types/workspace-entity-duplicate-criteria.type';
import { ApplicationEntity } from 'src/engine/core-modules/application/application.entity';
import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity';
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity';
import { type ObjectStandardOverridesDTO } from 'src/engine/metadata-modules/object-metadata/dtos/object-standard-overrides.dto';
import { FieldPermissionEntity } from 'src/engine/metadata-modules/object-permission/field-permission/field-permission.entity';
import { ObjectPermissionEntity } from 'src/engine/metadata-modules/object-permission/object-permission.entity';
import { ApplicationEntity } from 'src/engine/core-modules/application/application.entity';
@Entity('objectMetadata')
@Unique('IDX_OBJECT_METADATA_NAME_SINGULAR_WORKSPACE_ID_UNIQUE', [
@@ -96,6 +96,7 @@ export class ObjectMetadataEntity implements Required<ObjectMetadataEntity> {
@Column({ nullable: true, type: 'varchar' })
shortcut: string | null;
// TODO: This should not be nullable - legacy field introduced when label identifier was nullable
@Column({ nullable: true, type: 'uuid' })
labelIdentifierFieldMetadataId: string | null;
@@ -114,12 +114,23 @@ export class WorkspaceMigrationBuildOrchestratorService {
await this.workspaceMigrationV2ObjectActionsBuilderService.validateAndBuild(
{
buildOptions,
dependencyOptimisticFlatEntityMaps: undefined,
// TODO That's hacky also not reliable ?
// dependencyOptimisticFlatEntityMaps: {
// flatFieldMetadataMaps:
// flatFieldMetadataMaps?.to ?? EMPTY_FLAT_ENTITY_MAPS,
// },
// Note: That's a hacky way to allow validating object against field metadatas, not optimal
dependencyOptimisticFlatEntityMaps: {
flatFieldMetadataMaps: {
byId: {
...dependencyAllFlatEntityMaps?.flatFieldMetadataMaps?.byId,
...flatFieldMetadataMaps?.from.byId,
...flatFieldMetadataMaps?.to.byId,
},
idByUniversalIdentifier: {
...dependencyAllFlatEntityMaps?.flatFieldMetadataMaps
?.idByUniversalIdentifier,
...flatFieldMetadataMaps?.from.idByUniversalIdentifier,
...flatFieldMetadataMaps?.to.idByUniversalIdentifier,
},
},
},
///
from: fromFlatObjectMetadataMaps,
to: toFlatObjectMetadataMaps,
workspaceId,
@@ -1,5 +1,6 @@
import { Injectable } from '@nestjs/common';
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
import { compareTwoFlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/utils/compare-two-flat-object-metadata.util';
import {
@@ -13,13 +14,13 @@ import {
WorkspaceMigrationObjectActionV2,
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-object-action-v2';
import { FlatObjectMetadataValidatorService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-object-metadata-validator.service';
// export type ObjectMetadataRelatedFlatEntityMaps = Pick<
// AllFlatEntityMaps,
// 'flatFieldMetadataMaps'
// >;
export type ObjectMetadataRelatedFlatEntityMaps = Pick<
AllFlatEntityMaps,
'flatFieldMetadataMaps'
>;
// TODO find a solution in order to handle label indentifier validation
export type ObjectMetadataRelatedFlatEntityMaps = undefined;
// export type ObjectMetadataRelatedFlatEntityMaps = undefined;
@Injectable()
export class WorkspaceMigrationV2ObjectActionsBuilderService extends WorkspaceEntityMigrationBuilderV2Service<
@@ -134,12 +135,12 @@ export class WorkspaceMigrationV2ObjectActionsBuilderService extends WorkspaceEn
>
| undefined
> {
const objectMetadataUpdatedProperties = compareTwoFlatObjectMetadata({
const flatObjectPropertiesUpdates = compareTwoFlatObjectMetadata({
fromFlatObjectMetadata,
toFlatObjectMetadata,
});
if (objectMetadataUpdatedProperties.length === 0) {
if (flatObjectPropertiesUpdates.length === 0) {
return undefined;
}
@@ -149,6 +150,7 @@ export class WorkspaceMigrationV2ObjectActionsBuilderService extends WorkspaceEn
flatObjectMetadataToValidate: toFlatObjectMetadata,
optimisticFlatObjectMetadataMaps,
dependencyOptimisticFlatEntityMaps,
flatObjectPropertiesUpdates: flatObjectPropertiesUpdates,
});
if (validationResult.errors.length > 0) {
@@ -161,7 +163,7 @@ export class WorkspaceMigrationV2ObjectActionsBuilderService extends WorkspaceEn
const updateObjectAction: UpdateObjectAction = {
type: 'update_object',
objectMetadataId: toFlatObjectMetadata.id,
updates: objectMetadataUpdatedProperties,
updates: flatObjectPropertiesUpdates,
};
return {
@@ -9,17 +9,19 @@ export type CreateObjectAction = {
flatFieldMetadatas: FlatFieldMetadata[];
};
export type FlatObjectPropertiesUpdates = Array<
{
[P in FlatObjectMetadataPropertiesToCompare]: PropertyUpdate<
FlatObjectMetadata,
P
>;
}[FlatObjectMetadataPropertiesToCompare]
>;
export type UpdateObjectAction = {
type: 'update_object';
objectMetadataId: string;
updates: Array<
{
[P in FlatObjectMetadataPropertiesToCompare]: PropertyUpdate<
FlatObjectMetadata,
P
>;
}[FlatObjectMetadataPropertiesToCompare]
>;
updates: FlatObjectPropertiesUpdates;
};
export type DeleteObjectAction = {
@@ -4,17 +4,15 @@ import { t } from '@lingui/core/macro';
import { isDefined } from 'twenty-shared/utils';
import { FlatEntityMaps } from 'src/engine/core-modules/common/types/flat-entity-maps.type';
import { FlatObjectMetadataValidationError } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata-validation-error.type';
import { 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 { validateFlatObjectMetadataIdentifiers } from 'src/engine/metadata-modules/flat-object-metadata/validators/utils/validate-flat-object-metadata-identifiers.util';
import { validateFlatObjectMetadataNameAndLabels } from 'src/engine/metadata-modules/flat-object-metadata/validators/utils/validate-flat-object-metadata-name-and-labels.util';
import { ObjectMetadataExceptionCode } from 'src/engine/metadata-modules/object-metadata/object-metadata.exception';
import { isStandardMetadata } from 'src/engine/metadata-modules/utils/is-standard-metadata.util';
import { doesOtherObjectWithSameNameExists } from 'src/engine/metadata-modules/utils/validate-no-other-object-with-same-name-exists-or-throw.util';
import { ObjectMetadataRelatedFlatEntityMaps } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/object/services/workspace-migration-v2-object-actions-builder.service';
import { FailedFlatEntityValidation } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/builders/types/failed-flat-entity-validation.type';
import { WorkspaceMigrationBuilderOptions } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-builder-options.type';
import { FlatObjectPropertiesUpdates } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-object-action-v2';
export type ValidateOneObjectMetadataArgs = {
flatObjectMetadataToValidate: FlatObjectMetadata;
@@ -28,8 +26,11 @@ export class FlatObjectMetadataValidatorService {
public validateFlatObjectMetadataUpdate({
optimisticFlatObjectMetadataMaps,
flatObjectMetadataToValidate: updatedFlatObjectMetadata,
// dependencyOptimisticFlatEntityMaps: { flatFieldMetadataMaps },
}: ValidateOneObjectMetadataArgs): FailedFlatEntityValidation<FlatObjectMetadata> {
dependencyOptimisticFlatEntityMaps: { flatFieldMetadataMaps },
flatObjectPropertiesUpdates,
}: ValidateOneObjectMetadataArgs & {
flatObjectPropertiesUpdates: FlatObjectPropertiesUpdates;
}): FailedFlatEntityValidation<FlatObjectMetadata> {
const validationResult: FailedFlatEntityValidation<FlatObjectMetadata> = {
type: 'update_object',
errors: [],
@@ -57,24 +58,34 @@ export class FlatObjectMetadataValidatorService {
};
validationResult.errors.push(
...this.validateFlatObjectMetadataNameAndLabels({
...validateFlatObjectMetadataNameAndLabels({
optimisticFlatObjectMetadataMaps,
flatObjectMetadataToValidate: updatedFlatObjectMetadata,
}),
);
// const { objectFlatFieldMetadatas } =
// findObjectFieldsInFlatFieldMetadataMapsOrThrow({
// flatFieldMetadataMaps,
// flatObjectMetadata: existingFlatObjectMetadata,
// });
const labelIdentifierFieldMetadataIdUpdate =
flatObjectPropertiesUpdates.find(
(update) => update.property === 'labelIdentifierFieldMetadataId',
);
// validationResult.errors.push(
// ...validateFlatObjectMetadataIdentifiers({
// flatObjectMetadata: existingFlatObjectMetadata,
// objectFlatFieldMetadatas,
// }),
// );
// TODO remove this once we migrated labelIdentifierFieldMetadataId as non nullable
if (isDefined(labelIdentifierFieldMetadataIdUpdate)) {
if (!isDefined(labelIdentifierFieldMetadataIdUpdate.to)) {
validationResult.errors.push({
code: ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
message: 'labelIdentifierFieldMetadataId cannot be null',
userFriendlyMessage: t`Field label identifier is required`,
});
}
validationResult.errors.push(
...validateFlatObjectMetadataIdentifiers({
flatObjectMetadata: updatedFlatObjectMetadata,
flatFieldMetadataMaps,
}),
);
}
return validationResult;
}
@@ -142,7 +153,7 @@ export class FlatObjectMetadataValidatorService {
public async validateFlatObjectMetadataCreation({
optimisticFlatObjectMetadataMaps,
flatObjectMetadataToValidate,
// dependencyOptimisticFlatEntityMaps: { flatFieldMetadataMaps },
dependencyOptimisticFlatEntityMaps: { flatFieldMetadataMaps },
}: ValidateOneObjectMetadataArgs): Promise<
FailedFlatEntityValidation<FlatObjectMetadata>
> {
@@ -177,20 +188,14 @@ export class FlatObjectMetadataValidatorService {
});
}
// const { objectFlatFieldMetadatas } =
// findObjectFieldsInFlatFieldMetadataMapsOrThrow({
// flatFieldMetadataMaps,
// flatObjectMetadata: flatObjectMetadataToValidate,
// });
// objectValidationResult.errors.push(
// ...validateFlatObjectMetadataIdentifiers({
// flatObjectMetadata: flatObjectMetadataToValidate,
// objectFlatFieldMetadatas,
// }),
// );
objectValidationResult.errors.push(
...this.validateFlatObjectMetadataNameAndLabels({
...validateFlatObjectMetadataIdentifiers({
flatObjectMetadata: flatObjectMetadataToValidate,
flatFieldMetadataMaps,
}),
);
objectValidationResult.errors.push(
...validateFlatObjectMetadataNameAndLabels({
optimisticFlatObjectMetadataMaps,
flatObjectMetadataToValidate,
}),
@@ -198,56 +203,4 @@ export class FlatObjectMetadataValidatorService {
return objectValidationResult;
}
private 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;
}
}
@@ -8,7 +8,7 @@ import {
import { AllFlatEntityMaps } from 'src/engine/core-modules/common/types/all-flat-entity-maps.type';
import { deleteFlatEntityFromFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/delete-flat-entity-from-flat-entity-maps-or-throw.util';
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
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 { isCompositeFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-composite-flat-field-metadata.util';
import { isEnumFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-enum-flat-field-metadata.util';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
@@ -90,11 +90,10 @@ export class DeleteObjectActionHandlerService extends WorkspaceMigrationRunnerAc
schemaName,
tableName,
});
const { objectFlatFieldMetadatas } =
findObjectFieldsInFlatFieldMetadataMapsOrThrow({
flatFieldMetadataMaps,
flatObjectMetadata,
});
const { objectFlatFieldMetadatas } = findObjectFlatFieldMetadatasOrThrow({
flatFieldMetadataMaps,
flatObjectMetadata,
});
const enumOrCompositeFlatFieldMetadatas = objectFlatFieldMetadatas.filter(
(field) =>
isEnumFlatFieldMetadata(field) || isCompositeFlatFieldMetadata(field),
@@ -8,7 +8,7 @@ import {
import { 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 { replaceFlatEntityInFlatEntityMapsOrThrow } from 'src/engine/core-modules/common/utils/replace-flat-entity-in-flat-entity-maps-or-throw.util';
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 { isCompositeFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-composite-flat-field-metadata.util';
import { isEnumFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-enum-flat-field-metadata.util';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
@@ -122,7 +122,7 @@ export class UpdateObjectActionHandlerService extends WorkspaceMigrationRunnerAc
});
const { objectFlatFieldMetadatas } =
findObjectFieldsInFlatFieldMetadataMapsOrThrow({
findObjectFlatFieldMetadatasOrThrow({
flatFieldMetadataMaps,
flatObjectMetadata: updatedObjectMetadata,
});
@@ -1,5 +1,113 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Object metadata update should fail when labelIdentifier is not a TEXT or NAME field 1`] = `
[
{
"extensions": {
"code": "BAD_USER_INPUT",
"errors": {
"cronTrigger": [],
"databaseEventTrigger": [],
"fieldMetadata": [],
"index": [],
"objectMetadata": [
{
"errors": [
{
"code": "INVALID_OBJECT_INPUT",
"message": "labelIdentifierFieldMetadataId validation failed: field type not compatible",
"userFriendlyMessage": "Field cannot be used as label identifier",
},
],
"flatEntityMinimalInformation": {
"id": Any<String>,
"namePlural": "whatevers",
"nameSingular": "whatever",
},
"status": "fail",
"type": "update_object",
},
],
"routeTrigger": [],
"serverlessFunction": [],
"view": [],
"viewField": [],
},
"message": "Validation failed for 0 object(s) and 0 field(s)",
"summary": {
"invalidCronTrigger": 0,
"invalidDatabaseEventTrigger": 0,
"invalidFieldMetadata": 0,
"invalidIndex": 0,
"invalidObjectMetadata": 0,
"invalidRouteTrigger": 0,
"invalidServerlessFunction": 0,
"invalidView": 0,
"invalidViewField": 0,
"totalErrors": 0,
},
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
},
"message": "Multiple validation errors occurred while updating object",
"name": "GraphQLError",
},
]
`;
exports[`Object metadata update should fail when labelIdentifier is not a known field metadata id 1`] = `
[
{
"extensions": {
"code": "BAD_USER_INPUT",
"errors": {
"cronTrigger": [],
"databaseEventTrigger": [],
"fieldMetadata": [],
"index": [],
"objectMetadata": [
{
"errors": [
{
"code": "INVALID_OBJECT_INPUT",
"message": "labelIdentifierFieldMetadataId validation failed: related field metadata not found",
"userFriendlyMessage": "Field declared as label identifier not found",
},
],
"flatEntityMinimalInformation": {
"id": Any<String>,
"namePlural": "whatevers",
"nameSingular": "whatever",
},
"status": "fail",
"type": "update_object",
},
],
"routeTrigger": [],
"serverlessFunction": [],
"view": [],
"viewField": [],
},
"message": "Validation failed for 0 object(s) and 0 field(s)",
"summary": {
"invalidCronTrigger": 0,
"invalidDatabaseEventTrigger": 0,
"invalidFieldMetadata": 0,
"invalidIndex": 0,
"invalidObjectMetadata": 0,
"invalidRouteTrigger": 0,
"invalidServerlessFunction": 0,
"invalidView": 0,
"invalidViewField": 0,
"totalErrors": 0,
},
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
},
"message": "Multiple validation errors occurred while updating object",
"name": "GraphQLError",
},
]
`;
exports[`Object metadata update should fail when labelIdentifier is not a uuid 1`] = `
[
{
@@ -15,3 +123,57 @@ exports[`Object metadata update should fail when labelIdentifier is not a uuid 1
},
]
`;
exports[`Object metadata update should fail when labelIdentifier is null 1`] = `
[
{
"extensions": {
"code": "BAD_USER_INPUT",
"errors": {
"cronTrigger": [],
"databaseEventTrigger": [],
"fieldMetadata": [],
"index": [],
"objectMetadata": [
{
"errors": [
{
"code": "INVALID_OBJECT_INPUT",
"message": "labelIdentifierFieldMetadataId cannot be null",
"userFriendlyMessage": "Field label identifier is required",
},
],
"flatEntityMinimalInformation": {
"id": Any<String>,
"namePlural": "whatevers",
"nameSingular": "whatever",
},
"status": "fail",
"type": "update_object",
},
],
"routeTrigger": [],
"serverlessFunction": [],
"view": [],
"viewField": [],
},
"message": "Validation failed for 0 object(s) and 0 field(s)",
"summary": {
"invalidCronTrigger": 0,
"invalidDatabaseEventTrigger": 0,
"invalidFieldMetadata": 0,
"invalidIndex": 0,
"invalidObjectMetadata": 0,
"invalidRouteTrigger": 0,
"invalidServerlessFunction": 0,
"invalidView": 0,
"invalidViewField": 0,
"totalErrors": 0,
},
"userFriendlyMessage": "Validation failed for 0 object(s) and 0 field(s)",
},
"message": "Multiple validation errors occurred while updating object",
"name": "GraphQLError",
},
]
`;
@@ -29,25 +29,24 @@ const labelIdentifierFailingTestsUseCase: CreateOneObjectMetadataItemTestingCont
labelIdentifierFieldMetadataId: 'not-a-uuid',
},
},
// TODO prastoin fix label identifier validators
// {
// title: 'when labelIdentifier is not a known field metadata id',
// context: {
// labelIdentifierFieldMetadataId: '42422020-f49c-4159-8751-76a24f47b360',
// },
// },
// {
// title: 'when labelIdentifier is null',
// context: {
// labelIdentifierFieldMetadataId: null as any,
// },
// },
// {
// title: 'when labelIdentifier is not a TEXT or NAME field',
// context: ({ numberFieldMetadataId }) => ({
// labelIdentifierFieldMetadataId: numberFieldMetadataId,
// }),
// },
{
title: 'when labelIdentifier is not a known field metadata id',
context: {
labelIdentifierFieldMetadataId: '42422020-f49c-4159-8751-76a24f47b360',
},
},
{
title: 'when labelIdentifier is null',
context: {
labelIdentifierFieldMetadataId: null as any,
},
},
{
title: 'when labelIdentifier is not a TEXT or NAME field',
context: ({ numberFieldMetadataId }) => ({
labelIdentifierFieldMetadataId: numberFieldMetadataId,
}),
},
];
const allTestsUseCases = [...labelIdentifierFailingTestsUseCase];