Add integration tests for /metadata + fix relation deletion (#8706)

In this PR

1. Add integration tests for /metadata (master issue:
https://github.com/twentyhq/twenty/issues/8719)
2. Fix relation deletion: index on "from" object was not deleted,
impeding creation of a new relation between the same two objects A and B
after relation between A and B was deleted
This commit is contained in:
Marie
2024-11-26 10:00:36 +01:00
committed by GitHub
parent 49526937fa
commit 7bde2006c5
11 changed files with 597 additions and 9 deletions

View File

@ -1,7 +1,7 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { isDefined } from 'class-validator';
import isEmpty from 'lodash.isempty';
import { Repository } from 'typeorm';
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
@ -19,6 +19,7 @@ import {
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
import { WorkspaceMigrationService } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.service';
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
import { isDefined } from 'src/utils/is-defined';
@Injectable()
export class IndexMetadataService {
@ -43,6 +44,10 @@ export class IndexMetadataService {
(fieldMetadata) => fieldMetadata.name as string,
);
if (isEmpty(columnNames)) {
throw new Error('Column names must not be empty');
}
const indexName = `IDX_${generateDeterministicIndexName([tableName, ...columnNames])}`;
let result: IndexMetadataEntity;
@ -98,6 +103,44 @@ export class IndexMetadataService {
);
}
async deleteIndexMetadata(
workspaceId: string,
objectMetadata: ObjectMetadataEntity,
fieldMetadataToIndex: Partial<FieldMetadataEntity>[],
) {
const tableName = computeObjectTargetTable(objectMetadata);
const columnNames: string[] = fieldMetadataToIndex.map(
(fieldMetadata) => fieldMetadata.name as string,
);
if (isEmpty(columnNames)) {
throw new Error('Column names must not be empty');
}
const indexName = `IDX_${generateDeterministicIndexName([tableName, ...columnNames])}`;
const indexMetadata = await this.indexMetadataRepository.findOne({
where: {
name: indexName,
objectMetadataId: objectMetadata.id,
workspaceId,
},
});
if (!indexMetadata) {
throw new Error(`Index metadata with name ${indexName} not found`);
}
try {
await this.indexMetadataRepository.delete(indexMetadata.id);
} catch (error) {
throw new Error(
`Failed to delete index metadata with name ${indexName} (error: ${error.message})`,
);
}
}
async createIndexCreationMigration(
workspaceId: string,
objectMetadata: ObjectMetadataEntity,

View File

@ -36,6 +36,7 @@ import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
import { WorkspaceMigrationRunnerService } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.service';
import { BASE_OBJECT_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
import { isDefined } from 'src/utils/is-defined';
import {
RelationMetadataEntity,
@ -137,22 +138,20 @@ export class RelationMetadataService extends TypeOrmQueryService<RelationMetadat
);
}
const deletedFieldMetadata = toObjectMetadata.fields.find(
const deletedAtFieldMetadata = toObjectMetadata.fields.find(
(fieldMetadata) =>
fieldMetadata.standardId === BASE_OBJECT_STANDARD_FIELD_IDS.deletedAt,
);
if (!deletedFieldMetadata) {
throw new RelationMetadataException(
`Deleted field metadata not found`,
RelationMetadataExceptionCode.RELATION_METADATA_NOT_FOUND,
);
}
this.throwIfDeletedAtFieldMetadataNotFound(deletedAtFieldMetadata);
await this.indexMetadataService.createIndexMetadata(
relationMetadataInput.workspaceId,
toObjectMetadata,
[foreignKeyFieldMetadata, deletedFieldMetadata],
[
foreignKeyFieldMetadata,
deletedAtFieldMetadata as FieldMetadataEntity<'default'>,
],
false,
false,
);
@ -441,6 +440,24 @@ export class RelationMetadataService extends TypeOrmQueryService<RelationMetadat
columnName,
);
const deletedAtFieldMetadata = await this.fieldMetadataRepository.findOneBy(
{
objectMetadataId: relationMetadata.toObjectMetadataId,
name: 'deletedAt',
},
);
this.throwIfDeletedAtFieldMetadataNotFound(deletedAtFieldMetadata);
await this.indexMetadataService.deleteIndexMetadata(
workspaceId,
relationMetadata.toObjectMetadata,
[
foreignKeyFieldMetadata,
deletedAtFieldMetadata as FieldMetadataEntity<'default'>,
],
);
await this.workspaceMigrationRunnerService.executeMigrationFromPendingMigrations(
relationMetadata.workspaceId,
);
@ -554,4 +571,15 @@ export class RelationMetadataService extends TypeOrmQueryService<RelationMetadat
],
);
}
private throwIfDeletedAtFieldMetadataNotFound(
deletedAtFieldMetadata?: FieldMetadataEntity<'default'> | null,
) {
if (!isDefined(deletedAtFieldMetadata)) {
throw new RelationMetadataException(
`Deleted field metadata not found`,
RelationMetadataExceptionCode.RELATION_METADATA_NOT_FOUND,
);
}
}
}