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
@@ -56,44 +56,80 @@ describe('generateFakeFormResponse', () => {
objectMetadataMaps: mockObjectMetadataMaps,
});
expect(result).toEqual({
name: {
isLeaf: true,
label: 'Name',
type: FieldMetadataType.TEXT,
value: 'My text',
icon: undefined,
},
age: {
isLeaf: true,
label: 'Age',
type: FieldMetadataType.NUMBER,
value: 20,
icon: undefined,
},
company: {
isLeaf: false,
label: 'Company',
value: {
_outputSchemaType: 'RECORD',
fields: {},
object: {
isLeaf: true,
label: 'Company',
fieldIdName: 'id',
icon: 'test-company-icon',
nameSingular: 'company',
value: 'A company',
expect(result).toMatchInlineSnapshot(`
{
"age": {
"icon": undefined,
"isLeaf": true,
"label": "Age",
"type": "NUMBER",
"value": 20,
},
"company": {
"isLeaf": false,
"label": "Company",
"value": {
"_outputSchemaType": "RECORD",
"fields": {
"domainName": {
"icon": "test-field-icon",
"isLeaf": false,
"label": "Domain Name",
"type": "LINKS",
"value": {
"primaryLinkLabel": {
"isLeaf": true,
"label": "Primary Link Label",
"type": "TEXT",
"value": "My text",
},
"primaryLinkUrl": {
"isLeaf": true,
"label": "Primary Link Url",
"type": "TEXT",
"value": "My text",
},
"secondaryLinks": {
"isLeaf": true,
"label": "Secondary Links",
"type": "RAW_JSON",
"value": null,
},
},
},
"name": {
"icon": "test-field-icon",
"isLeaf": true,
"label": "Name",
"type": "TEXT",
"value": "My text",
},
},
date: {
isLeaf: true,
label: 'Date',
type: FieldMetadataType.DATE,
value: 'mm/dd/yyyy',
icon: undefined,
"object": {
"fieldIdName": "id",
"icon": "test-company-icon",
"isLeaf": true,
"label": "Company",
"nameSingular": "company",
"value": "A company",
},
});
},
},
"date": {
"icon": undefined,
"isLeaf": true,
"label": "Date",
"type": "DATE",
"value": "mm/dd/yyyy",
},
"name": {
"icon": undefined,
"isLeaf": true,
"label": "Name",
"type": "TEXT",
"value": "My text",
},
}
`);
});
});
@@ -1,71 +1,100 @@
import { FieldMetadataType } from 'twenty-shared/types';
import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface';
import { shouldGenerateFieldFakeValue } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/should-generate-field-fake-value';
import { getMockFieldMetadataEntity } from 'src/utils/__test__/get-field-metadata-entity.mock';
describe('shouldGenerateFieldFakeValue', () => {
it('should return true for active non-system fields', () => {
const field = {
const field = getMockFieldMetadataEntity({
workspaceId: '20202020-0000-0000-0000-000000000000',
objectMetadataId: '20202020-0000-0000-0000-000000000001',
id: '20202020-0000-0000-0000-000000000002',
isSystem: false,
isActive: true,
type: FieldMetadataType.TEXT,
name: 'testField',
label: 'Test Field',
isNullable: true,
isLabelSyncedWithName: true,
createdAt: new Date(),
updatedAt: new Date(),
} as FieldMetadataInterface;
});
expect(shouldGenerateFieldFakeValue(field)).toBe(true);
});
it('should return true for system id field', () => {
const field = {
const field = getMockFieldMetadataEntity({
workspaceId: '20202020-0000-0000-0000-000000000000',
objectMetadataId: '20202020-0000-0000-0000-000000000001',
id: '20202020-0000-0000-0000-000000000003',
isSystem: true,
isActive: true,
type: FieldMetadataType.UUID,
name: 'id',
label: 'ID',
isNullable: false,
isLabelSyncedWithName: true,
createdAt: new Date(),
updatedAt: new Date(),
} as FieldMetadataInterface;
});
expect(shouldGenerateFieldFakeValue(field)).toBe(true);
});
it('should return false for inactive fields', () => {
const field = {
const field = getMockFieldMetadataEntity({
workspaceId: '20202020-0000-0000-0000-000000000000',
objectMetadataId: '20202020-0000-0000-0000-000000000001',
id: '20202020-0000-0000-0000-000000000004',
isSystem: false,
isActive: false,
type: FieldMetadataType.TEXT,
name: 'testField',
label: 'Test Field',
isNullable: true,
isLabelSyncedWithName: true,
createdAt: new Date(),
updatedAt: new Date(),
} as FieldMetadataInterface;
});
expect(shouldGenerateFieldFakeValue(field)).toBe(false);
});
it('should return false for system fields (except id)', () => {
const field = {
const field = getMockFieldMetadataEntity({
workspaceId: '20202020-0000-0000-0000-000000000000',
objectMetadataId: '20202020-0000-0000-0000-000000000001',
id: '20202020-0000-0000-0000-000000000005',
isSystem: true,
isActive: true,
type: FieldMetadataType.TEXT,
name: 'testField',
label: 'Test Field',
isNullable: true,
isLabelSyncedWithName: true,
createdAt: new Date(),
updatedAt: new Date(),
} as FieldMetadataInterface;
});
expect(shouldGenerateFieldFakeValue(field)).toBe(false);
});
it('should return false for relation fields', () => {
const field = {
const field = getMockFieldMetadataEntity({
workspaceId: '20202020-0000-0000-0000-000000000000',
objectMetadataId: '20202020-0000-0000-0000-000000000001',
id: '20202020-0000-0000-0000-000000000006',
isSystem: false,
isActive: true,
type: FieldMetadataType.RELATION,
name: 'testField',
label: 'Test Field',
isNullable: true,
isLabelSyncedWithName: true,
createdAt: new Date(),
updatedAt: new Date(),
} as FieldMetadataInterface;
});
expect(shouldGenerateFieldFakeValue(field)).toBe(false);
});
@@ -28,7 +28,7 @@ export const generateObjectRecordFields = ({
acc[field.name] = generateFakeField({
type: field.type,
label: field.label,
icon: field.icon,
icon: field.icon ?? undefined,
});
return acc;
@@ -49,7 +49,7 @@ export const generateObjectRecordFields = ({
acc[field.name] = {
isLeaf: false,
icon: field.icon,
icon: field.icon ?? undefined,
label: field.label,
value: generateFakeObjectRecord({
objectMetadataInfo: {
@@ -1,18 +1,19 @@
import { FieldMetadataType } from 'twenty-shared/types';
import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface';
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
const isManyToOneRelationField = (field: FieldMetadataInterface) =>
const isManyToOneRelationField = (field: FieldMetadataEntity) =>
(field as FieldMetadataEntity<FieldMetadataType.RELATION>).settings
?.relationType === 'MANY_TO_ONE';
export const shouldGenerateFieldFakeValue = (field: FieldMetadataInterface) => {
// TODO refactor
export const shouldGenerateFieldFakeValue = <T extends FieldMetadataType>(
field: FieldMetadataEntity<T>,
) => {
return (
field.isActive &&
(!field.isSystem || field.name === 'id' || field.name === 'userEmail') &&
(field.type !== FieldMetadataType.RELATION ||
isManyToOneRelationField(field))
isManyToOneRelationField(field as unknown as FieldMetadataEntity))
);
};