refactor: rename ObjectMetadataItem to EnrichedObjectMetadataItem and clean up metadata flows (#18830)

## Summary

- Renames `ObjectMetadataItem` to `EnrichedObjectMetadataItem` across
the entire frontend (~440 files) to clarify that this type includes
derived fields (`readableFields`, `updatableFields`, nested `fields[]`,
`indexMetadatas[]`) computed at read time from the metadata store
- Creates `splitObjectMetadataGqlResponse` that goes directly from a
GraphQL `ObjectMetadataItemsQuery` response to flat store items
(combining the old
`mapPaginatedObjectMetadataItemsToObjectMetadataItems` +
`splitObjectMetadataItemWithRelated` two-step flow into one call)
- Removes `ObjectMetadataItemWithRelated` type and all "WithRelated"
naming
- Renames `generatedMockObjectMetadataItems` to
`generateTestEnrichedObjectMetadataItemsMock` to make it clear this is
test-only enriched data
- Deletes `useLoadMockedObjectMetadataItems` hook (consolidated into
`useLoadMockedMinimalMetadata`)
- Ensures nothing destined for the metadata store computes
`readableFields`/`updatableFields` (preventing the localStorage bloat
from #18809)

## Type hierarchy (before → after)

**Before:**
```
ObjectMetadataItemsQuery → mapPaginated → ObjectMetadataItemWithRelated → enrich → ObjectMetadataItem
                                        → split → FlatObjectMetadataItem (store)
```

**After:**
```
ObjectMetadataItemsQuery → splitObjectMetadataGqlResponse → FlatObjectMetadataItem (store)
                         → mapPaginated + enrich (tests only) → EnrichedObjectMetadataItem
```

## Test plan

- [x] `npx nx typecheck twenty-front` passes
- [x] `npx nx test twenty-front` passes (767 suites, 4505 tests)
- [x] `npx nx lint twenty-front` passes
- [ ] CI checks pass


Made with [Cursor](https://cursor.com)
This commit is contained in:
Charles Bochet
2026-03-22 15:53:47 +01:00
committed by GitHub
parent 96c5728ed0
commit d5a7dec117
440 changed files with 1409 additions and 1285 deletions
@@ -1,8 +1,8 @@
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem';
import { COMMAND_MENU_DEFAULT_ICON } from '@/workflow/workflow-trigger/constants/CommandMenuDefaultIcon';
import { getManualTriggerDefaultSettings } from '@/workflow/workflow-trigger/utils/getManualTriggerDefaultSettings';
const mockObjectMetadataItems: ObjectMetadataItem[] = [
const mockObjectMetadataItems: EnrichedObjectMetadataItem[] = [
{
id: 'company-id',
nameSingular: 'company',
@@ -12,7 +12,7 @@ const mockObjectMetadataItems: ObjectMetadataItem[] = [
icon: 'IconBuilding',
fields: [],
createdAt: new Date(),
} as unknown as ObjectMetadataItem,
} as unknown as EnrichedObjectMetadataItem,
];
describe('getManualTriggerDefaultSettings', () => {
@@ -76,7 +76,7 @@ describe('getManualTriggerDefaultSettings', () => {
});
it('should use the first object metadata item', () => {
const multipleObjects: ObjectMetadataItem[] = [
const multipleObjects: EnrichedObjectMetadataItem[] = [
...mockObjectMetadataItems,
{
id: 'person-id',
@@ -86,7 +86,7 @@ describe('getManualTriggerDefaultSettings', () => {
labelPlural: 'People',
icon: 'IconUser',
fields: [],
} as unknown as ObjectMetadataItem,
} as unknown as EnrichedObjectMetadataItem,
];
const result = getManualTriggerDefaultSettings({
@@ -1,6 +1,6 @@
import { COMMAND_MENU_DEFAULT_ICON } from '@/workflow/workflow-trigger/constants/CommandMenuDefaultIcon';
import { DatabaseTriggerDefaultLabel } from '@/workflow/workflow-trigger/constants/DatabaseTriggerDefaultLabel';
import { generatedMockObjectMetadataItems } from '~/testing/utils/generatedMockObjectMetadataItems';
import { getTestEnrichedObjectMetadataItemsMock } from '~/testing/utils/getTestEnrichedObjectMetadataItemsMock';
import { getTriggerDefaultDefinition } from '@/workflow/workflow-trigger/utils/getTriggerDefaultDefinition';
describe('getTriggerDefaultDefinition', () => {
@@ -19,13 +19,14 @@ describe('getTriggerDefaultDefinition', () => {
getTriggerDefaultDefinition({
defaultLabel: DatabaseTriggerDefaultLabel.RECORD_IS_CREATED,
type: 'DATABASE_EVENT',
activeNonSystemObjectMetadataItems: generatedMockObjectMetadataItems,
activeNonSystemObjectMetadataItems:
getTestEnrichedObjectMetadataItemsMock(),
}),
).toStrictEqual({
type: 'DATABASE_EVENT',
name: 'Record is created',
settings: {
eventName: `${generatedMockObjectMetadataItems[0].nameSingular}.created`,
eventName: `${getTestEnrichedObjectMetadataItemsMock()[0].nameSingular}.created`,
outputSchema: {},
},
position: {
@@ -40,13 +41,14 @@ describe('getTriggerDefaultDefinition', () => {
getTriggerDefaultDefinition({
defaultLabel: DatabaseTriggerDefaultLabel.RECORD_IS_UPDATED,
type: 'DATABASE_EVENT',
activeNonSystemObjectMetadataItems: generatedMockObjectMetadataItems,
activeNonSystemObjectMetadataItems:
getTestEnrichedObjectMetadataItemsMock(),
}),
).toStrictEqual({
type: 'DATABASE_EVENT',
name: 'Record is updated',
settings: {
eventName: `${generatedMockObjectMetadataItems[0].nameSingular}.updated`,
eventName: `${getTestEnrichedObjectMetadataItemsMock()[0].nameSingular}.updated`,
outputSchema: {},
},
position: {
@@ -61,13 +63,14 @@ describe('getTriggerDefaultDefinition', () => {
getTriggerDefaultDefinition({
defaultLabel: DatabaseTriggerDefaultLabel.RECORD_IS_DELETED,
type: 'DATABASE_EVENT',
activeNonSystemObjectMetadataItems: generatedMockObjectMetadataItems,
activeNonSystemObjectMetadataItems:
getTestEnrichedObjectMetadataItemsMock(),
}),
).toStrictEqual({
type: 'DATABASE_EVENT',
name: 'Record is deleted',
settings: {
eventName: `${generatedMockObjectMetadataItems[0].nameSingular}.deleted`,
eventName: `${getTestEnrichedObjectMetadataItemsMock()[0].nameSingular}.deleted`,
outputSchema: {},
},
position: {
@@ -82,7 +85,8 @@ describe('getTriggerDefaultDefinition', () => {
getTriggerDefaultDefinition({
defaultLabel: 'Launch manually',
type: 'MANUAL',
activeNonSystemObjectMetadataItems: generatedMockObjectMetadataItems,
activeNonSystemObjectMetadataItems:
getTestEnrichedObjectMetadataItemsMock(),
}),
).toStrictEqual({
type: 'MANUAL',
@@ -109,7 +113,8 @@ describe('getTriggerDefaultDefinition', () => {
getTriggerDefaultDefinition({
defaultLabel: 'On a schedule',
type: 'CRON',
activeNonSystemObjectMetadataItems: generatedMockObjectMetadataItems,
activeNonSystemObjectMetadataItems:
getTestEnrichedObjectMetadataItemsMock(),
}),
).toStrictEqual({
type: 'CRON',
@@ -131,7 +136,8 @@ describe('getTriggerDefaultDefinition', () => {
getTriggerDefaultDefinition({
defaultLabel: 'Webhook',
type: 'WEBHOOK',
activeNonSystemObjectMetadataItems: generatedMockObjectMetadataItems,
activeNonSystemObjectMetadataItems:
getTestEnrichedObjectMetadataItemsMock(),
}),
).toStrictEqual({
type: 'WEBHOOK',
@@ -153,7 +159,8 @@ describe('getTriggerDefaultDefinition', () => {
getTriggerDefaultDefinition({
defaultLabel: DatabaseTriggerDefaultLabel.RECORD_IS_CREATED,
type: 'unknown' as any,
activeNonSystemObjectMetadataItems: generatedMockObjectMetadataItems,
activeNonSystemObjectMetadataItems:
getTestEnrichedObjectMetadataItemsMock(),
});
}).toThrow('Unknown type: unknown');
});
@@ -1,4 +1,4 @@
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem';
import { type WorkflowManualTriggerSettings } from '@/workflow/types/Workflow';
import { COMMAND_MENU_DEFAULT_ICON } from '@/workflow/workflow-trigger/constants/CommandMenuDefaultIcon';
import { assertUnreachable } from 'twenty-shared/utils';
@@ -10,7 +10,7 @@ export const getManualTriggerDefaultSettings = ({
isPinned,
}: {
availabilityType: 'GLOBAL' | 'SINGLE_RECORD' | 'BULK_RECORDS';
activeNonSystemObjectMetadataItems: ObjectMetadataItem[];
activeNonSystemObjectMetadataItems: EnrichedObjectMetadataItem[];
icon?: string;
isPinned?: boolean;
}): WorkflowManualTriggerSettings => {
@@ -1,4 +1,4 @@
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem';
import {
type WorkflowTrigger,
type WorkflowTriggerType,
@@ -15,7 +15,7 @@ export const getTriggerDefaultDefinition = ({
}: {
defaultLabel: string;
type: WorkflowTriggerType;
activeNonSystemObjectMetadataItems: ObjectMetadataItem[];
activeNonSystemObjectMetadataItems: EnrichedObjectMetadataItem[];
}): WorkflowTrigger => {
if (activeNonSystemObjectMetadataItems.length === 0) {
throw new Error(