fix IndexFieldMetadata availability in IndexMetadata/ObjectMetadata in front (#12886)

Context : 
- IndexFieldMetadata was no longer available on 'objects' gql query
([since this PR](https://github.com/twentyhq/twenty/pull/12785)). Then,
unicity checks on import do not work anymore.

Fix : 
- Add a dataloader logic in indexFieldMetadata
- Add extra check in unicity hook on import
This commit is contained in:
Etienne
2025-06-27 13:12:14 +02:00
committed by GitHub
parent c3f8a25d25
commit b80762b3e1
20 changed files with 225 additions and 71 deletions

View File

@ -81,9 +81,9 @@ describe('spreadsheetImportGetUnicityRowHook', () => {
const hook = spreadsheetImportGetUnicityRowHook(mockObjectMetadataItem);
const testData: ImportedStructuredRow<string>[] = [
{ 'Link URL (domainName)': 'duplicaTe.com', id: '1' },
{ 'Link URL (domainName)': 'duplicate.com ', id: '2' },
{ 'Link URL (domainName)': 'other.com', id: '3' },
{ 'Link URL (domainName)': 'duplicaTe.com' },
{ 'Link URL (domainName)': 'duplicate.com ' },
{ 'Link URL (domainName)': 'other.com' },
];
const addErrorMock = jest.fn();

View File

@ -1,12 +1,24 @@
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { isCompositeFieldType } from '@/object-record/object-filter-dropdown/utils/isCompositeFieldType';
import { getSubFieldOptionKey } from '@/object-record/spreadsheet-import/utils/getSubFieldOptionKey';
import { COMPOSITE_FIELD_SUB_FIELD_LABELS } from '@/settings/data-model/constants/CompositeFieldSubFieldLabel';
import { SETTINGS_COMPOSITE_FIELD_TYPE_CONFIGS } from '@/settings/data-model/constants/SettingsCompositeFieldTypeConfigs';
import {
ImportedStructuredRow,
SpreadsheetImportRowHook,
} from '@/spreadsheet-import/types';
import { isDefined } from 'twenty-shared/utils';
import { t } from '@lingui/core/macro';
import { isNonEmptyString } from '@sniptt/guards';
import { FieldMetadataType } from 'twenty-shared/types';
import {
isDefined,
lowercaseUrlAndRemoveTrailingSlash,
} from 'twenty-shared/utils';
type Column = {
columnName: string;
fieldType: FieldMetadataType;
};
export const spreadsheetImportGetUnicityRowHook = (
objectMetadataItem: ObjectMetadataItem,
@ -15,8 +27,8 @@ export const spreadsheetImportGetUnicityRowHook = (
(indexMetadata) => indexMetadata.isUnique,
);
const uniqueConstraintFields = [
['id'],
const uniqueConstraintsWithColumnNames: Column[][] = [
[{ columnName: 'id', fieldType: FieldMetadataType.UUID }],
...uniqueConstraints.map((indexMetadata) =>
indexMetadata.indexFieldMetadatas.flatMap((indexField) => {
const field = objectMetadataItem.fields.find(
@ -35,12 +47,13 @@ export const spreadsheetImportGetUnicityRowHook = (
(subField) => subField.isIncludedInUniqueConstraint,
);
return uniqueSubFields.map((subField) =>
getSubFieldOptionKey(field, subField.subFieldName),
);
return uniqueSubFields.map((subField) => ({
columnName: getSubFieldOptionKey(field, subField.subFieldName),
fieldType: field.type,
}));
}
return [field.name];
return [{ columnName: field.name, fieldType: field.type }];
}),
),
];
@ -50,9 +63,13 @@ export const spreadsheetImportGetUnicityRowHook = (
return row;
}
uniqueConstraintFields.forEach((uniqueConstraint) => {
uniqueConstraintsWithColumnNames.forEach((uniqueConstraint) => {
const rowUniqueValues = getUniqueValues(row, uniqueConstraint);
if (!isNonEmptyString(rowUniqueValues)) {
return row;
}
const duplicateRows = table.filter(
(r) => getUniqueValues(r, uniqueConstraint) === rowUniqueValues,
);
@ -61,10 +78,10 @@ export const spreadsheetImportGetUnicityRowHook = (
return row;
}
uniqueConstraint.forEach((field) => {
if (isDefined(row[field])) {
addError(field, {
message: `This ${field} value already exists in your import data`,
uniqueConstraint.forEach(({ columnName }) => {
if (isDefined(row[columnName])) {
addError(columnName, {
message: t`This ${columnName} value already exists in your import data`,
level: 'error',
});
}
@ -79,9 +96,24 @@ export const spreadsheetImportGetUnicityRowHook = (
const getUniqueValues = (
row: ImportedStructuredRow<string>,
uniqueConstraint: string[],
uniqueConstraint: Column[],
) => {
return uniqueConstraint
.map((field) => row?.[field]?.toString().trim().toLowerCase())
.map(({ columnName, fieldType }) => {
// need to ensure the primary link url is processed before import as on server side
if (
fieldType === FieldMetadataType.LINKS &&
columnName.includes(
COMPOSITE_FIELD_SUB_FIELD_LABELS[FieldMetadataType.LINKS]
.primaryLinkUrl,
)
) {
return lowercaseUrlAndRemoveTrailingSlash(
row?.[columnName]?.toString().trim() || '',
);
}
return row?.[columnName]?.toString().trim().toLowerCase();
})
.join('');
};