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:
+17
-6
@@ -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,
|
||||
|
||||
+10
-8
@@ -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 {
|
||||
|
||||
+10
-8
@@ -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 = {
|
||||
|
||||
+38
-85
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+5
-6
@@ -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),
|
||||
|
||||
+2
-2
@@ -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,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user