Unique field - add unique property creation/deletion on field (#13539)

Done : 
- add isUnique prop availability on gql update/create fieldMetadata
resolvers
- add unique index creation logic at update & creation
- add unique index deletion logic at update
- update unique index if field name updated
- edge cases : can't have default value and unique fields / standard
default value excluded from index (where clause) / can't have composite
unique fields / can't create unique fields on MORPH


closes https://github.com/twentyhq/core-team-issues/issues/1222
This commit is contained in:
Etienne
2025-08-08 17:42:50 +02:00
committed by GitHub
parent 415f57ffa5
commit 7ce96049b8
18 changed files with 1485 additions and 53 deletions
@@ -0,0 +1,35 @@
import { FieldMetadataType } from 'twenty-shared/types';
import { isValidUniqueFieldDefaultValueCombination } from 'src/engine/metadata-modules/field-metadata/utils/is-valid-unique-input.util';
describe('isValidUniqueFieldDefaultValueCombination', () => {
it('should return true if the field has a custom default value and is not unique', () => {
const result = isValidUniqueFieldDefaultValueCombination({
defaultValue: "'custom value'",
isUnique: false,
type: FieldMetadataType.TEXT,
});
expect(result).toBe(true);
});
it('should return true if the field has standard default value and is unique', () => {
const result = isValidUniqueFieldDefaultValueCombination({
defaultValue: "''",
isUnique: true,
type: FieldMetadataType.TEXT,
});
expect(result).toBe(true);
});
it('should return false if the field has custom default value and is unique', () => {
const result = isValidUniqueFieldDefaultValueCombination({
defaultValue: "'custom value'",
isUnique: true,
type: FieldMetadataType.TEXT,
});
expect(result).toBe(false);
});
});
@@ -0,0 +1,21 @@
import { isDeepStrictEqual } from 'util';
import { type FieldMetadataType } from 'twenty-shared/types';
import { type FieldMetadataDefaultValue } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface';
import { generateDefaultValue } from 'src/engine/metadata-modules/field-metadata/utils/generate-default-value';
export const isValidUniqueFieldDefaultValueCombination = ({
defaultValue,
isUnique,
type,
}: {
defaultValue: FieldMetadataDefaultValue;
isUnique: boolean;
type: FieldMetadataType;
}) => {
const defaultDefaultValue = generateDefaultValue(type);
return !isUnique || isDeepStrictEqual(defaultValue, defaultDefaultValue);
};
@@ -35,6 +35,7 @@ export const prepareCustomFieldMetadataForCreation = (
fieldMetadataInput?.relationCreationPayload?.targetObjectMetadataId,
defaultValue,
...options,
isUnique: fieldMetadataInput.isUnique ?? false,
isActive: true,
isCustom: true,
settings: fieldMetadataInput.settings,