6629092645
# Introduction Moving validation directly in the builder that has the perfect granularity to do it. When importing we won't have to infer and dispatch on the operation nature ( update delete create ) and validate accordingly ## Objects Only migrated object validation for the moment even though create object involves a validate flat field metadata creation call too ## TODO - improve `otherFlatObjectMetadataMapsToValidate` naming too vague ## Next - handle fields validation within fields actions build - Unit test coverage validation issue on builder and validate - integration test plugging with new feature flag ## Manual tested - Update - Delete - Create TODO
237 lines
9.6 KiB
TypeScript
237 lines
9.6 KiB
TypeScript
import { Injectable } from '@nestjs/common';
|
|
|
|
import { isDefined } from 'twenty-shared/utils';
|
|
|
|
import { EMPTY_FLAT_OBJECT_METADATA_MAPS } from 'src/engine/metadata-modules/flat-object-metadata-maps/constant/empty-flat-object-metadata-maps.constant';
|
|
import { addFlatObjectMetadataToFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/add-flat-object-metadata-to-flat-object-metadata-maps-or-throw.util';
|
|
import { deleteFieldFromFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/delete-field-from-flat-object-metadata-maps-or-throw.util';
|
|
import { deleteObjectFromFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/delete-object-from-flat-object-metadata-maps-or-throw.util';
|
|
import { getSubFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/get-sub-flat-object-metadata-maps-or-throw.util';
|
|
import { getSubFlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/get-sub-flat-object-metadata-maps.util';
|
|
import { replaceFlatObjectMetadataInFlatObjectMetadataMapsOrThrow } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/replace-flat-object-metadata-in-flat-object-metadata-maps-or-throw.util';
|
|
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
|
import { fromCreateObjectInputToFlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-create-object-input-to-flat-object-metadata.util';
|
|
import { fromDeleteObjectInputToFlatFieldMetadatasToDelete } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-delete-object-input-to-flat-field-metadatas-to-delete.util';
|
|
import { fromFlatObjectMetadataToObjectMetadataDto } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-flat-object-metadata-to-object-metadata-dto.util';
|
|
import { fromUpdateObjectInputToFlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-update-object-input-to-flat-object-metadata.util';
|
|
import { CreateObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/create-object.input';
|
|
import { DeleteOneObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/delete-object.input';
|
|
import { ObjectMetadataDTO } from 'src/engine/metadata-modules/object-metadata/dtos/object-metadata.dto';
|
|
import { UpdateOneObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/update-object.input';
|
|
import {
|
|
ObjectMetadataException,
|
|
ObjectMetadataExceptionCode,
|
|
} from 'src/engine/metadata-modules/object-metadata/object-metadata.exception';
|
|
import { WorkspaceMetadataCacheService } from 'src/engine/metadata-modules/workspace-metadata-cache/services/workspace-metadata-cache.service';
|
|
import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-validate-build-and-run-service';
|
|
|
|
@Injectable()
|
|
export class ObjectMetadataServiceV2 {
|
|
constructor(
|
|
private readonly workspaceMetadataCacheService: WorkspaceMetadataCacheService,
|
|
private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService,
|
|
) {}
|
|
|
|
async updateOne({
|
|
updateObjectInput,
|
|
workspaceId,
|
|
}: {
|
|
workspaceId: string;
|
|
updateObjectInput: UpdateOneObjectInput;
|
|
}): Promise<ObjectMetadataDTO> {
|
|
const { flatObjectMetadataMaps: existingFlatObjectMetadataMaps } =
|
|
await this.workspaceMetadataCacheService.getExistingOrRecomputeFlatObjectMetadataMaps(
|
|
{
|
|
workspaceId,
|
|
},
|
|
);
|
|
|
|
const optimisticallyUpdatedFlatObjectMetadata =
|
|
fromUpdateObjectInputToFlatObjectMetadata({
|
|
existingFlatObjectMetadataMaps,
|
|
updateObjectInput,
|
|
});
|
|
|
|
const fromFlatObjectMetadataMaps = getSubFlatObjectMetadataMapsOrThrow({
|
|
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
|
objectMetadataIds: [optimisticallyUpdatedFlatObjectMetadata.id],
|
|
});
|
|
const toFlatObjectMetadataMaps =
|
|
replaceFlatObjectMetadataInFlatObjectMetadataMapsOrThrow({
|
|
flatObjectMetadata: optimisticallyUpdatedFlatObjectMetadata,
|
|
flatObjectMetadataMaps: fromFlatObjectMetadataMaps,
|
|
});
|
|
|
|
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
|
{
|
|
fromFlatObjectMetadataMaps,
|
|
toFlatObjectMetadataMaps,
|
|
buildOptions: {
|
|
isSystemBuild: false,
|
|
inferDeletionFromMissingObjectFieldIndex: false,
|
|
},
|
|
workspaceId,
|
|
errorMessage:
|
|
'Multiple validation errors occurred while updating object',
|
|
},
|
|
);
|
|
|
|
const { flatObjectMetadataMaps: recomputedFlatObjectMetadataMaps } =
|
|
await this.workspaceMetadataCacheService.getExistingOrRecomputeFlatObjectMetadataMaps(
|
|
{
|
|
workspaceId,
|
|
},
|
|
);
|
|
|
|
const updatedFlatObjectMetadata =
|
|
recomputedFlatObjectMetadataMaps.byId[
|
|
optimisticallyUpdatedFlatObjectMetadata.id
|
|
];
|
|
|
|
if (!isDefined(updatedFlatObjectMetadata)) {
|
|
throw new ObjectMetadataException(
|
|
'Updated object metadata not found in recomputed cache',
|
|
ObjectMetadataExceptionCode.INTERNAL_SERVER_ERROR,
|
|
);
|
|
}
|
|
|
|
return fromFlatObjectMetadataToObjectMetadataDto(updatedFlatObjectMetadata);
|
|
}
|
|
|
|
async deleteOne({
|
|
deleteObjectInput,
|
|
workspaceId,
|
|
}: {
|
|
deleteObjectInput: DeleteOneObjectInput;
|
|
workspaceId: string;
|
|
}): Promise<ObjectMetadataDTO> {
|
|
const { flatObjectMetadataMaps: existingFlatObjectMetadataMaps } =
|
|
await this.workspaceMetadataCacheService.getExistingOrRecomputeFlatObjectMetadataMaps(
|
|
{
|
|
workspaceId,
|
|
},
|
|
);
|
|
|
|
const { flatFieldMetadatasToDelete, flatObjectMetadataToDelete } =
|
|
fromDeleteObjectInputToFlatFieldMetadatasToDelete({
|
|
deleteObjectInput,
|
|
existingFlatObjectMetadataMaps,
|
|
});
|
|
const { id: objectMetadataToDeleteId } = flatObjectMetadataToDelete;
|
|
|
|
const impactedObjectMetadataIds = Array.from(
|
|
new Set(
|
|
flatFieldMetadatasToDelete.map(
|
|
(flatFieldMetadata) => flatFieldMetadata.objectMetadataId,
|
|
),
|
|
),
|
|
);
|
|
|
|
const fromFlatObjectMetadataMaps = getSubFlatObjectMetadataMapsOrThrow({
|
|
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
|
objectMetadataIds: impactedObjectMetadataIds,
|
|
});
|
|
|
|
const toFlatObjectMetadataMaps = flatFieldMetadatasToDelete
|
|
.filter(
|
|
(flatFieldMetadataToDelete) =>
|
|
flatFieldMetadataToDelete.objectMetadataId !==
|
|
objectMetadataToDeleteId,
|
|
)
|
|
.reduce(
|
|
(flatObjectMetadataMaps, flatFieldMetadata) =>
|
|
deleteFieldFromFlatObjectMetadataMapsOrThrow({
|
|
fieldMetadataId: flatFieldMetadata.id,
|
|
objectMetadataId: flatFieldMetadata.objectMetadataId,
|
|
flatObjectMetadataMaps,
|
|
}),
|
|
deleteObjectFromFlatObjectMetadataMapsOrThrow({
|
|
flatObjectMetadataMaps: fromFlatObjectMetadataMaps,
|
|
objectMetadataId: objectMetadataToDeleteId,
|
|
}),
|
|
);
|
|
|
|
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
|
{
|
|
fromFlatObjectMetadataMaps,
|
|
toFlatObjectMetadataMaps,
|
|
buildOptions: {
|
|
inferDeletionFromMissingObjectFieldIndex: true,
|
|
isSystemBuild: false,
|
|
},
|
|
workspaceId,
|
|
errorMessage:
|
|
'Multiple validation errors occurred while deleting object',
|
|
},
|
|
);
|
|
|
|
return fromFlatObjectMetadataToObjectMetadataDto(
|
|
flatObjectMetadataToDelete,
|
|
);
|
|
}
|
|
|
|
async createOne({
|
|
createObjectInput,
|
|
workspaceId,
|
|
}: {
|
|
createObjectInput: Omit<CreateObjectInput, 'workspaceId'>;
|
|
workspaceId: string;
|
|
}): Promise<FlatObjectMetadata> {
|
|
const { flatObjectMetadataMaps: existingFlatObjectMetadataMaps } =
|
|
await this.workspaceMetadataCacheService.getExistingOrRecomputeFlatObjectMetadataMaps(
|
|
{
|
|
workspaceId,
|
|
},
|
|
);
|
|
|
|
const flatObjectMetadataToCreate =
|
|
fromCreateObjectInputToFlatObjectMetadata({
|
|
createObjectInput,
|
|
workspaceId,
|
|
});
|
|
|
|
const fromFlatObjectMetadataMaps =
|
|
getSubFlatObjectMetadataMaps({
|
|
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
|
|
objectMetadataIds: [flatObjectMetadataToCreate.id],
|
|
}) ?? EMPTY_FLAT_OBJECT_METADATA_MAPS;
|
|
|
|
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
|
{
|
|
fromFlatObjectMetadataMaps,
|
|
toFlatObjectMetadataMaps:
|
|
addFlatObjectMetadataToFlatObjectMetadataMapsOrThrow({
|
|
flatObjectMetadataMaps: EMPTY_FLAT_OBJECT_METADATA_MAPS,
|
|
flatObjectMetadata: flatObjectMetadataToCreate,
|
|
}),
|
|
buildOptions: {
|
|
isSystemBuild: false,
|
|
inferDeletionFromMissingObjectFieldIndex: false,
|
|
},
|
|
workspaceId,
|
|
errorMessage:
|
|
'Multiple validation errors occurred while creating object',
|
|
},
|
|
);
|
|
|
|
const { flatObjectMetadataMaps: recomputedFlatObjectMetadataMaps } =
|
|
await this.workspaceMetadataCacheService.getExistingOrRecomputeFlatObjectMetadataMaps(
|
|
{
|
|
workspaceId,
|
|
},
|
|
);
|
|
|
|
const createdFlatObjectMetadata =
|
|
recomputedFlatObjectMetadataMaps.byId[flatObjectMetadataToCreate.id];
|
|
|
|
if (!isDefined(createdFlatObjectMetadata)) {
|
|
throw new ObjectMetadataException(
|
|
'Created object metadata not found in recomputed cache',
|
|
ObjectMetadataExceptionCode.OBJECT_METADATA_NOT_FOUND,
|
|
);
|
|
}
|
|
|
|
return createdFlatObjectMetadata;
|
|
}
|
|
}
|