Update unique fields on standard field - include soft deleted records (#14562)

Done : 
- migrate standard unique fields (add : -old)
- deactivate defaultWhereClause
- restore soft deleted if upserted (same for contact creation in mailing
sync)

fixes https://github.com/twentyhq/twenty/issues/14443

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Etienne
2025-10-11 15:50:29 +02:00
committed by GitHub
parent c681fb7fb6
commit 560e24fa4b
26 changed files with 1119 additions and 316 deletions
@@ -388,7 +388,7 @@ export class GraphqlQueryCreateManyResolverService extends GraphqlQueryBaseResol
const savedRecords = await repository.updateMany(
partialRecordsToUpdateWithoutCreatedByUpdate.map((record) => ({
criteria: record.id,
partialEntity: record,
partialEntity: { ...record, deletedAt: null },
})),
undefined,
columnsToReturn,
@@ -269,7 +269,10 @@ export class IndexMetadataService {
queryRunner,
}: {
workspaceId: string;
objectMetadata: ObjectMetadataEntity;
objectMetadata: Pick<
ObjectMetadataEntity,
'nameSingular' | 'isCustom' | 'id'
>;
fieldMetadataToIndex: Partial<FieldMetadataEntity>[];
queryRunner?: QueryRunner;
}) {
@@ -19,7 +19,7 @@ export function WorkspaceIsUnique(): PropertyDecorator {
const columns = [propertyKey.toString()];
metadataArgsStorage.addIndexes({
name: `IDX_UNIQUE_${generateDeterministicIndexName([
name: `IDX_${generateDeterministicIndexName([
convertClassNameToObjectMetadataName(target.constructor.name),
...columns,
])}`,
@@ -46,9 +46,7 @@ describe('WorkspaceMigrationIndexFactory', () => {
expect(firstMigration.indexes[0].columns).toEqual(['simpleField']);
expect(firstMigration.indexes[0].type).toBe('BTREE');
expect(firstMigration.indexes[0].isUnique).toBe(true);
expect(firstMigration.indexes[0].where).toBe(
'"simpleField" != \'\' AND "deletedAt" IS NULL',
);
expect(firstMigration.indexes[0].where).toBe('"simpleField" != \'\'');
});
it('should create index migrations for relation fields', async () => {
@@ -88,7 +88,7 @@ export const createIndexMigration = async (
.filter(isDefined);
const defaultWhereClause = indexMetadata.isUnique
? `${columns.map((column) => `"${column}"`).join(" != '' AND ")} != '' AND "deletedAt" IS NULL`
? `${columns.map((column) => `"${column}"`).join(" != '' AND ")} != ''`
: null;
return {