Deprecate FieldMetadataInterface (#13264)

# Introduction

From the moment replaced the FieldMetadataInterface definition to:
```ts
import { FieldMetadataType } from 'twenty-shared/types';

import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';

export type FieldMetadataInterface<
  T extends FieldMetadataType = FieldMetadataType,
> = FieldMetadataEntity<T>;
```
After this PR merge will create a new one removing the type and
replacing it to `FieldMetadataEntity`.
Did not renamed it here to avoid conflicts on naming + type issues fixs
within the same PR

## Field metadata entity RELATION or MORPH
Relations fields cannot be null for those field metadata entity instance
anymore, but are never for the others see
`packages/twenty-server/src/engine/metadata-modules/field-metadata/types/field-metadata-entity-test.type.ts`
( introduced TypeScript tests )

## Concerns
- TS_VECTOR is the most at risk with the `generatedType` and
`asExpression` removal from interface

## What's next
- `FielMetadataInterface` removal and rename ( see introduction )
- Depcrecating `ObjectMetadataInterface`
- Refactor `FieldMetadataEntity` optional fiels to be nullable only
- TO DIG `never` occurences on settings, defaultValue etc
- Some interfaces will be replaced by the `FlatFieldMetadata` when
deprecating the current sync and comparators tools
This commit is contained in:
Paul Rastoin
2025-07-21 11:30:18 +02:00
committed by GitHub
parent c2a5f95675
commit 47b60bd49f
67 changed files with 1780 additions and 769 deletions

View File

@ -1,10 +1,15 @@
import { FieldMetadataType } from 'twenty-shared/types';
import { RelationOnDeleteAction } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-on-delete-action.interface';
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
import { FieldActorSource } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type';
import { FieldMetadataComplexOption } from 'src/engine/metadata-modules/field-metadata/dtos/options.input';
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
import { getMockFieldMetadataEntity } from 'src/utils/__test__/get-field-metadata-entity.mock';
export const FIELD_LINKS_MOCK_NAME = 'fieldLinks';
export const FIELD_CURRENCY_MOCK_NAME = 'fieldCurrency';
@ -13,194 +18,292 @@ export const FIELD_ACTOR_MOCK_NAME = 'fieldActor';
export const FIELD_FULL_NAME_MOCK_NAME = 'fieldFullName';
export const FIELD_PHONES_MOCK_NAME = 'fieldPhones';
export const fieldNumberMock = {
const workspaceId = '20202020-0000-0000-0000-000000000000';
const objectMetadataId = '20202020-0000-0000-0000-000000000001';
export const fieldNumberMock = getMockFieldMetadataEntity({
workspaceId,
objectMetadataId,
id: 'fieldNumberId',
name: 'fieldNumber',
type: FieldMetadataType.NUMBER,
label: 'Field Number',
isNullable: false,
defaultValue: null,
};
isLabelSyncedWithName: true,
createdAt: new Date(),
updatedAt: new Date(),
});
export const fieldTextMock = {
export const fieldTextMock = getMockFieldMetadataEntity({
workspaceId,
objectMetadataId,
id: 'fieldTextId',
name: 'fieldText',
type: FieldMetadataType.TEXT,
label: 'Field Text',
isNullable: true,
defaultValue: null,
};
isLabelSyncedWithName: true,
createdAt: new Date(),
updatedAt: new Date(),
});
export const fieldCurrencyMock = {
export const fieldCurrencyMock = getMockFieldMetadataEntity({
workspaceId,
objectMetadataId,
id: 'fieldCurrencyId',
name: FIELD_CURRENCY_MOCK_NAME,
type: FieldMetadataType.CURRENCY,
label: 'Field Currency',
isNullable: true,
defaultValue: { amountMicros: null, currencyCode: "''" },
};
isLabelSyncedWithName: true,
createdAt: new Date(),
updatedAt: new Date(),
});
export const fieldSelectMock = {
export const fieldSelectMock = getMockFieldMetadataEntity({
workspaceId,
objectMetadataId,
id: 'fieldSelectId',
name: 'fieldSelect',
type: FieldMetadataType.SELECT,
label: 'Field Select',
isNullable: true,
defaultValue: 'OPTION_1',
options: [
{
id: '9a519a86-422b-4598-88ae-78751353f683',
color: 'red',
label: 'Opt 1',
value: 'OPTION_1',
position: 0,
color: 'red',
},
{
id: '33f28d51-bc82-4e1d-ae4b-d9e4c0ed0ab4',
color: 'purple',
label: 'Opt 2',
value: 'OPTION_2',
position: 1,
color: 'purple',
},
],
};
isLabelSyncedWithName: true,
createdAt: new Date(),
updatedAt: new Date(),
});
const fieldMultiSelectMock = {
export const fieldMultiSelectMock = getMockFieldMetadataEntity({
workspaceId,
objectMetadataId,
id: 'fieldMultiSelectId',
name: 'fieldMultiSelect',
type: FieldMetadataType.MULTI_SELECT,
label: 'Field Multi Select',
isNullable: true,
defaultValue: "{'OPTION_1'}",
defaultValue: ['OPTION_1'],
options: [
{
id: '9a519a86-422b-4598-88ae-78751353f683',
color: 'red',
label: 'Opt 1',
value: 'OPTION_1',
position: 0,
color: 'red',
},
{
id: '33f28d51-bc82-4e1d-ae4b-d9e4c0ed0ab4',
color: 'purple',
label: 'Opt 2',
value: 'OPTION_2',
position: 1,
color: 'purple',
},
],
};
isLabelSyncedWithName: true,
createdAt: new Date(),
updatedAt: new Date(),
});
export const fieldRelationMock = {
export const fieldRelationMock = getMockFieldMetadataEntity({
workspaceId,
objectMetadataId,
id: 'fieldRelationId',
name: 'fieldRelation',
type: FieldMetadataType.RELATION,
label: 'Field Relation',
isNullable: true,
defaultValue: null,
settings: {
relationType: RelationType.MANY_TO_ONE,
joinColumnName: 'fieldRelationId',
onDelete: 'CASCADE',
onDelete: RelationOnDeleteAction.CASCADE,
},
relationTargetObjectMetadata: {
id: 'relationTargetObjectId',
nameSingular: 'relationTargetObject',
namePlural: 'relationTargetObjects',
},
} as ObjectMetadataEntity,
relationTargetFieldMetadata: {
id: 'relationTargetFieldId',
name: 'relationTargetField',
},
isNullable: true,
defaultValue: null,
};
} as FieldMetadataEntity,
isLabelSyncedWithName: true,
createdAt: new Date(),
updatedAt: new Date(),
});
const fieldLinksMock = {
export const fieldLinksMock = getMockFieldMetadataEntity({
workspaceId,
objectMetadataId,
id: 'fieldLinksId',
name: FIELD_LINKS_MOCK_NAME,
type: FieldMetadataType.LINKS,
label: 'Field Links',
isNullable: false,
defaultValue: [
{ primaryLinkLabel: '', primaryLinkUrl: '', secondaryLinks: [] },
],
};
defaultValue: {
primaryLinkLabel: '',
primaryLinkUrl: '',
secondaryLinks: [],
},
isLabelSyncedWithName: true,
createdAt: new Date(),
updatedAt: new Date(),
});
const fieldUuidMock = {
export const fieldUuidMock = getMockFieldMetadataEntity({
workspaceId,
objectMetadataId,
id: 'fieldUuidId',
name: 'fieldUuid',
type: FieldMetadataType.UUID,
label: 'Field UUID',
isNullable: true,
defaultValue: null,
};
isLabelSyncedWithName: true,
createdAt: new Date(),
updatedAt: new Date(),
});
const fieldDateTimeMock = {
export const fieldDateTimeMock = getMockFieldMetadataEntity({
workspaceId,
objectMetadataId,
id: 'fieldDateTimeId',
name: 'fieldDateTime',
type: FieldMetadataType.DATE_TIME,
label: 'Field Date Time',
isNullable: true,
defaultValue: null,
};
isLabelSyncedWithName: true,
createdAt: new Date(),
updatedAt: new Date(),
});
const fieldDateMock = {
export const fieldDateMock = getMockFieldMetadataEntity({
workspaceId,
objectMetadataId,
id: 'fieldDateId',
name: 'fieldDate',
type: FieldMetadataType.DATE,
label: 'Field Date',
isNullable: true,
defaultValue: null,
};
isLabelSyncedWithName: true,
createdAt: new Date(),
updatedAt: new Date(),
});
const fieldBooleanMock = {
export const fieldBooleanMock = getMockFieldMetadataEntity({
workspaceId,
objectMetadataId,
id: 'fieldBooleanId',
name: 'fieldBoolean',
type: FieldMetadataType.BOOLEAN,
label: 'Field Boolean',
isNullable: true,
defaultValue: null,
};
isLabelSyncedWithName: true,
createdAt: new Date(),
updatedAt: new Date(),
});
const fieldNumericMock = {
export const fieldNumericMock = getMockFieldMetadataEntity({
workspaceId,
objectMetadataId,
id: 'fieldNumericId',
name: 'fieldNumeric',
type: FieldMetadataType.NUMERIC,
label: 'Field Numeric',
isNullable: true,
defaultValue: null,
};
isLabelSyncedWithName: true,
createdAt: new Date(),
updatedAt: new Date(),
});
const fieldFullNameMock = {
export const fieldFullNameMock = getMockFieldMetadataEntity({
workspaceId,
objectMetadataId,
id: 'fieldFullNameId',
name: FIELD_FULL_NAME_MOCK_NAME,
type: FieldMetadataType.FULL_NAME,
label: 'Field Full Name',
isNullable: true,
defaultValue: { firstName: '', lastName: '' },
};
isLabelSyncedWithName: true,
createdAt: new Date(),
updatedAt: new Date(),
});
const fieldRatingMock = {
export const fieldRatingMock = getMockFieldMetadataEntity({
workspaceId,
objectMetadataId,
id: 'fieldRatingId',
name: 'fieldRating',
type: FieldMetadataType.RATING,
label: 'Field Rating',
isNullable: true,
defaultValue: 'RATING_1',
options: [
{
id: '9a519a86-422b-4598-88ae-78751353f683',
color: 'red',
label: 'Opt 1',
value: 'RATING_1',
position: 0,
color: 'red',
},
{
id: '33f28d51-bc82-4e1d-ae4b-d9e4c0ed0ab4',
color: 'purple',
label: 'Opt 2',
value: 'RATING_2',
position: 1,
color: 'purple',
},
],
};
] as FieldMetadataComplexOption[],
isLabelSyncedWithName: true,
createdAt: new Date(),
updatedAt: new Date(),
});
const fieldPositionMock = {
export const fieldPositionMock = getMockFieldMetadataEntity({
workspaceId,
objectMetadataId,
id: 'fieldPositionId',
name: 'fieldPosition',
type: FieldMetadataType.POSITION,
label: 'Field Position',
isNullable: true,
defaultValue: null,
};
isLabelSyncedWithName: true,
createdAt: new Date(),
updatedAt: new Date(),
});
const fieldAddressMock = {
export const fieldAddressMock = getMockFieldMetadataEntity({
workspaceId,
objectMetadataId,
id: 'fieldAddressId',
name: FIELD_ADDRESS_MOCK_NAME,
type: FieldMetadataType.ADDRESS,
label: 'Field Address',
isNullable: true,
defaultValue: {
addressStreet1: '',
@ -212,65 +315,105 @@ const fieldAddressMock = {
addressLat: null,
addressLng: null,
},
};
isLabelSyncedWithName: true,
createdAt: new Date(),
updatedAt: new Date(),
});
const fieldRawJsonMock = {
export const fieldRawJsonMock = getMockFieldMetadataEntity({
workspaceId,
objectMetadataId,
id: 'fieldRawJsonId',
name: 'fieldRawJson',
type: FieldMetadataType.RAW_JSON,
label: 'Field Raw JSON',
isNullable: true,
defaultValue: null,
};
isLabelSyncedWithName: true,
createdAt: new Date(),
updatedAt: new Date(),
});
const fieldRichTextMock = {
export const fieldRichTextMock = getMockFieldMetadataEntity({
workspaceId,
objectMetadataId,
id: 'fieldRichTextId',
name: 'fieldRichText',
type: FieldMetadataType.RICH_TEXT,
label: 'Field Rich Text',
isNullable: true,
defaultValue: null,
};
isLabelSyncedWithName: true,
createdAt: new Date(),
updatedAt: new Date(),
});
const fieldActorMock = {
export const fieldActorMock = getMockFieldMetadataEntity({
workspaceId,
objectMetadataId,
id: 'fieldActorId',
name: FIELD_ACTOR_MOCK_NAME,
type: FieldMetadataType.ACTOR,
label: 'Field Actor',
isNullable: true,
defaultValue: {
source: FieldActorSource.MANUAL,
name: '',
},
};
isLabelSyncedWithName: true,
createdAt: new Date(),
updatedAt: new Date(),
});
const fieldEmailsMock = {
export const fieldEmailsMock = getMockFieldMetadataEntity({
workspaceId,
objectMetadataId,
id: 'fieldEmailsId',
name: 'fieldEmails',
type: FieldMetadataType.EMAILS,
label: 'Field Emails',
isNullable: false,
defaultValue: [{ primaryEmail: '', additionalEmails: {} }],
};
defaultValue: {
primaryEmail: '',
additionalEmails: {},
},
isLabelSyncedWithName: true,
createdAt: new Date(),
updatedAt: new Date(),
});
const fieldArrayMock = {
export const fieldArrayMock = getMockFieldMetadataEntity({
workspaceId,
objectMetadataId,
id: 'fieldArrayId',
name: 'fieldArray',
type: FieldMetadataType.ARRAY,
label: 'Field Array',
isNullable: true,
defaultValue: null,
};
isLabelSyncedWithName: true,
createdAt: new Date(),
updatedAt: new Date(),
});
const fieldPhonesMock = {
export const fieldPhonesMock = getMockFieldMetadataEntity({
workspaceId,
objectMetadataId,
id: 'fieldPhonesId',
name: FIELD_PHONES_MOCK_NAME,
type: FieldMetadataType.PHONES,
label: 'Field Phones',
isNullable: false,
defaultValue: [
{
primaryPhoneNumber: '',
primaryPhoneCountryCode: '',
primaryPhoneCallingCode: '',
additionalPhones: {},
},
],
};
defaultValue: {
primaryPhoneNumber: '',
primaryPhoneCountryCode: '',
primaryPhoneCallingCode: '',
additionalPhones: {},
},
isLabelSyncedWithName: true,
createdAt: new Date(),
updatedAt: new Date(),
});
export const fields = [
fieldUuidMock,
@ -297,46 +440,47 @@ export const fields = [
fieldArrayMock,
];
export const objectMetadataItemMock = {
targetTableName: 'testingObject',
id: 'mockObjectId',
export const objectMetadataItemMock: ObjectMetadataEntity = {
id: objectMetadataId,
workspaceId,
nameSingular: 'objectName',
namePlural: 'objectsName',
namePlural: 'objectNames',
labelSingular: 'Object Name',
labelPlural: 'Object Names',
description: 'Object description',
icon: 'Icon123',
targetTableName: 'DEPRECATED',
isCustom: false,
isRemote: false,
isActive: true,
isSystem: false,
isAuditLogged: true,
isSearchable: true,
fields,
createdAt: new Date(),
updatedAt: new Date(),
} as ObjectMetadataEntity;
export const objectMetadataMapItemMock = {
id: 'mockObjectId',
icon: 'Icon123',
nameSingular: 'objectName',
namePlural: 'objectsName',
fieldsById: fields.reduce((acc, field) => {
// @ts-expect-error legacy noImplicitAny
acc[field.id] = field;
return acc;
}, {}),
fieldIdByName: fields.reduce((acc, field) => {
// @ts-expect-error legacy noImplicitAny
acc[field.name] = field;
return acc;
}, {}),
export const objectMetadataMapItemMock: ObjectMetadataItemWithFieldMaps = {
...objectMetadataItemMock,
fieldsById: fields.reduce(
(acc, field) => ({
...acc,
[field.id]: field,
}),
{},
),
fieldIdByName: fields.reduce(
(acc, field) => ({
...acc,
[field.name]: field.id,
}),
{},
),
fieldIdByJoinColumnName: {},
labelSingular: 'Object',
labelPlural: 'Objects',
workspaceId: 'mockWorkspaceId',
isCustom: false,
isSystem: false,
targetTableName: '',
indexMetadatas: [],
isActive: true,
isRemote: false,
isAuditLogged: false,
isSearchable: false,
} satisfies ObjectMetadataItemWithFieldMaps;
export const objectMetadataMapsMock = {
};
export const objectMetadataMapsMock: ObjectMetadataMaps = {
byId: {
[objectMetadataMapItemMock.id || 'mock-id']: objectMetadataMapItemMock,
},