ObjectMetadataServiceV2.deleteOne (#13871)

# Introduction
- Implementing the delete one for the new object metadata service v2.
- Handling relation fields and fields in the first place to finally
remove the object
- puting back updatedAt and createdAt in flat metadatas
- duplicate criteria addition for flat object
- removing datasource id from object metadata dto
This commit is contained in:
Paul Rastoin
2025-08-13 11:20:17 +02:00
committed by GitHub
parent 98e1a325a8
commit e8e81957a6
44 changed files with 863 additions and 473 deletions
@@ -9,6 +9,8 @@ type FlatObjectMetadataOverrides = Required<
export const getFlatObjectMetadataMock = (
overrides: FlatObjectMetadataOverrides,
): FlatObjectMetadata => {
const createdAt = new Date();
return {
flatFieldMetadatas: [],
flatIndexMetadatas: [],
@@ -33,6 +35,9 @@ export const getFlatObjectMetadataMock = (
standardOverrides: null,
targetTableName: '',
workspaceId: faker.string.uuid(),
createdAt,
updatedAt: createdAt,
duplicateCriteria: null,
...overrides,
};
};
@@ -1,6 +1,7 @@
import { Injectable } from '@nestjs/common';
import { t } from '@lingui/core/macro';
import { isDefined } from 'twenty-shared/utils';
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';
@@ -31,6 +32,60 @@ export class FlatObjectMetadataValidatorService {
private readonly flatFieldMetadataValidatorService: FlatFieldMetadataValidatorService,
) {}
public validateFlatObjectMetadataDeletion({
existingFlatObjectMetadataMaps,
objectMetadataToDeleteId,
}: {
existingFlatObjectMetadataMaps: FlatObjectMetadataMaps;
objectMetadataToDeleteId: string;
}) {
const errors: FailedFlatObjectMetadataValidationExceptions[] = [];
const flatObjectMetadataToDelete =
existingFlatObjectMetadataMaps.byId[objectMetadataToDeleteId];
if (!isDefined(flatObjectMetadataToDelete)) {
errors.push(
new ObjectMetadataException(
t`Object does not exist`,
ObjectMetadataExceptionCode.OBJECT_METADATA_NOT_FOUND,
),
);
} else {
if (flatObjectMetadataToDelete.isRemote) {
errors.push(
new ObjectMetadataException(
t`Remote objects are not supported yet`,
ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
),
);
}
if (
flatObjectMetadataToDelete.standardId !== null &&
!flatObjectMetadataToDelete.isCustom
) {
errors.push(
new ObjectMetadataException(
t`Standard objects cannot be deleted`,
ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
),
);
}
if (flatObjectMetadataToDelete.isActive) {
errors.push(
new ObjectMetadataException(
t`Active objects cannot be deleted`,
ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
),
);
}
}
return errors;
}
public async validateFlatObjectMetadataCreation({
existingFlatObjectMetadataMaps,
flatObjectMetadataToValidate,
@@ -20,11 +20,7 @@ type ObjectMetadataRelationProperties = ExtractRecordTypeOrmRelationProperties<
export type FlatObjectMetadata = Omit<
ObjectMetadataEntity,
| ObjectMetadataRelationProperties
| 'dataSourceId'
| 'createdAt'
| 'updatedAt'
| 'duplicateCriteria'
ObjectMetadataRelationProperties | 'dataSourceId'
> & {
uniqueIdentifier: string;
flatIndexMetadatas: FlatIndexMetadata[];
@@ -9,10 +9,10 @@ import { type CreateObjectInput } from 'src/engine/metadata-modules/object-metad
import { buildDefaultFlatFieldMetadataForCustomObject } from 'src/engine/metadata-modules/object-metadata/utils/build-default-fields-for-custom-object.util';
export const fromCreateObjectInputToFlatObjectMetadata = ({
objectMetadataInput: rawCreateObjectInput,
createObjectInput: rawCreateObjectInput,
workspaceId,
}: {
objectMetadataInput: Omit<CreateObjectInput, 'workspaceId'>;
createObjectInput: Omit<CreateObjectInput, 'workspaceId'>;
workspaceId: string;
}): FlatObjectMetadata => {
const createObjectInput =
@@ -36,7 +36,12 @@ export const fromCreateObjectInputToFlatObjectMetadata = ({
workspaceId,
});
const createdAt = new Date();
return {
createdAt,
updatedAt: createdAt,
duplicateCriteria: null,
description: createObjectInput.description ?? null,
flatFieldMetadatas: Object.values(baseCustomFlatFieldMetadatas),
flatIndexMetadatas: [],
@@ -0,0 +1,75 @@
import { FieldMetadataType } from 'twenty-shared/types';
import {
isDefined,
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties,
} from 'twenty-shared/utils';
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
import { getRelationFlatFieldMetadatasOrThrow } from 'src/engine/metadata-modules/flat-field-metadata/utils/get-relation-flat-field-metadatas-or-throw.util';
import { isFlatFieldMetadataEntityOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util';
import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
import { findFlatObjectMetadataInFlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/utils/find-flat-object-metadata-in-flat-object-metadata-maps.util';
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
import { type DeleteOneObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/delete-object.input';
import {
ObjectMetadataException,
ObjectMetadataExceptionCode,
} from 'src/engine/metadata-modules/object-metadata/object-metadata.exception';
export const fromDeleteObjectInputToFlatFieldMetadatasToDelete = ({
deleteObjectInput: rawDeleteObjectInput,
existingFlatObjectMetadataMaps,
}: {
deleteObjectInput: DeleteOneObjectInput;
existingFlatObjectMetadataMaps: FlatObjectMetadataMaps;
}): {
flatFieldMetadatasToDelete: FlatFieldMetadata[];
flatObjectMetadataToDelete: FlatObjectMetadata;
} => {
const { id: objectMetadataToDeleteId } =
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties(
rawDeleteObjectInput,
['id'],
);
const flatObjectMetadataToDelete =
findFlatObjectMetadataInFlatObjectMetadataMaps({
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
objectMetadataId: objectMetadataToDeleteId,
});
if (!isDefined(flatObjectMetadataToDelete)) {
throw new ObjectMetadataException(
'Object to delete not found',
ObjectMetadataExceptionCode.OBJECT_METADATA_NOT_FOUND,
);
}
const flatFieldMetadatasToDelete =
flatObjectMetadataToDelete.flatFieldMetadatas.flatMap(
(flatFieldMetadata) => {
if (
isFlatFieldMetadataEntityOfType(
flatFieldMetadata,
FieldMetadataType.RELATION,
) ||
isFlatFieldMetadataEntityOfType(
flatFieldMetadata,
FieldMetadataType.MORPH_RELATION,
)
) {
return getRelationFlatFieldMetadatasOrThrow({
existingFlatObjectMetadataMaps,
flatFieldMetadata,
});
}
return flatFieldMetadata;
},
);
return {
flatFieldMetadatasToDelete,
flatObjectMetadataToDelete,
};
};
@@ -0,0 +1,28 @@
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
import { type ObjectMetadataDTO } from 'src/engine/metadata-modules/object-metadata/dtos/object-metadata.dto';
export const fromFlatObjectMetadataToObjectMetadataDto = (
flatObjectMetadata: FlatObjectMetadata,
): ObjectMetadataDTO => {
const {
createdAt,
updatedAt,
description,
icon,
standardOverrides,
shortcut,
duplicateCriteria,
...rest
} = flatObjectMetadata;
return {
...rest,
createdAt: new Date(createdAt),
updatedAt: new Date(updatedAt),
description: description ?? undefined,
icon: icon ?? undefined,
standardOverrides: standardOverrides ?? undefined,
shortcut: shortcut ?? undefined,
duplicateCriteria: duplicateCriteria ?? undefined,
};
};