System view tooling explicit params key naming (#23506)

# Introduction
View field system always result from a field existence, the application
universal identifier should be the related field one
Same but for views and object

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/23506?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
This commit is contained in:
Paul Rastoin
2026-07-29 16:33:40 +02:00
committed by GitHub
parent 33970dd45d
commit 4ec65ed08d
22 changed files with 192 additions and 67 deletions

View File

@@ -1,16 +1,22 @@
---
title: Targeting System Fields
description: Reference auto-created system fields like createdAt or updatedAt from views and other entities with getFieldUniversalIdentifier.
title: Targeting System Metadata
description: Resolve the deterministic universal identifiers of the metadata Twenty provisions automatically on every object, so your app can reference it without hardcoding.
icon: "gears"
---
Every object in Twenty ships with a set of **system fields** that you never declare yourself. They are created automatically by the server when the object is provisioned:
Every object in Twenty comes with **system metadata** you never declare yourself, such as a set of fields and a main list view with its columns. The server creates all of it when the object is provisioned, and the set grows as Twenty does.
Because you don't declare it, there's no `universalIdentifier` constant for you to import. Instead, the server **derives** each identifier deterministically, and `twenty-sdk` exposes the same derivation so your manifest can resolve the exact value the server uses.
## System fields
The scalar fields present on every object, none of which you declare with [`defineField()`](/developers/extend/apps/data/extending-objects):
`id`, `createdAt`, `updatedAt`, `deletedAt`, `createdBy`, `updatedBy`, `position`, `searchVector`
Because you don't declare these fields with [`defineField()`](/developers/extend/apps/data/extending-objects), there's no `universalIdentifier` constant for you to import. So how do you reference `createdAt` as a column in a [view](/developers/extend/apps/layout/views)?
So how do you reference `createdAt` as a column in a [view](/developers/extend/apps/layout/views)?
## The problem
### The problem
Since Twenty 2.19, a system field's universal identifier is **derived deterministically** by the server from three inputs: the application universal identifier, the object universal identifier, and the field name. Inventing an id and hardcoding it won't work: it matches nothing on the server, and the sync rejects the dangling reference:
@@ -18,7 +24,7 @@ Since Twenty 2.19, a system field's universal identifier is **derived determinis
Dev sync failed: viewField: INVALID_VIEW_DATA: Field metadata not found
```
## The solution
### The solution
<Note>
`getFieldUniversalIdentifier` is available from `twenty-sdk` 2.21 onward.
@@ -40,7 +46,7 @@ const createdAtFieldId = getFieldUniversalIdentifier({
- `objectUniversalIdentifier` is the identifier of the object the field belongs to.
- `name` is the system field name, one of the values listed above.
## Example: a createdAt column in a view
### Example: a createdAt column in a view
The typical case is adding a `createdAt` column to a view of one of your custom objects. Resolve the field id and reference it as any other `fieldMetadataUniversalIdentifier`:
@@ -86,7 +92,7 @@ The same resolved id works anywhere a `fieldMetadataUniversalIdentifier` is expe
inputs change, and avoids drift if the derivation ever evolves.
</Note>
## System relation fields
### System relation fields
<Note>
`getSystemRelationFieldUniversalIdentifier` is available from `twenty-sdk`
@@ -132,18 +138,70 @@ const attachmentTargetRocketFieldId =
As with scalar system fields, the resolved id works anywhere a `fieldMetadataUniversalIdentifier` is expected.
## System views
<Note>
`getSystemViewUniversalIdentifier` and `getSystemViewFieldUniversalIdentifier`
are available from `twenty-sdk` 2.26 onward and require a Twenty server on
2.26 or later.
</Note>
The server also provisions a **system view** on every object: the main list view (`All {objectLabelPlural}`, keyed on `ViewKey.INDEX`), with one column per displayable field. Like system relation fields, their identifiers are derived **name-free**, so renaming an object or a field never changes them.
Use `getSystemViewUniversalIdentifier` to resolve the view:
```ts
import { getSystemViewUniversalIdentifier, ViewKey } from 'twenty-sdk/define';
const rocketIndexViewId = getSystemViewUniversalIdentifier({
objectMetadataApplicationUniversalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER,
objectUniversalIdentifier: ROCKET_OBJECT_UNIVERSAL_IDENTIFIER,
viewKey: ViewKey.INDEX,
});
```
- `objectMetadataApplicationUniversalIdentifier` is the application owning the **object**, which is what the view is namespaced by.
- `objectUniversalIdentifier` is the object the view lists.
- `viewKey` is the system view key, `ViewKey.INDEX` today.
The resolved id works anywhere a `viewUniversalIdentifier` is expected, such as a [`NavigationMenuItemType.VIEW`](/developers/extend/apps/layout/navigation-menu-items) sidebar entry. To simply open an object's main list, prefer `NavigationMenuItemType.OBJECT` with `targetObjectUniversalIdentifier`: it needs no derivation.
`getSystemViewFieldUniversalIdentifier` resolves a single **column** on a system view, from the view and the field it displays:
```ts
import { getSystemViewFieldUniversalIdentifier } from 'twenty-sdk/define';
const rocketNameColumnId = getSystemViewFieldUniversalIdentifier({
fieldMetadataApplicationUniversalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER,
viewUniversalIdentifier: rocketIndexViewId,
fieldMetadataUniversalIdentifier: ROCKET_NAME_FIELD_UNIVERSAL_IDENTIFIER,
});
```
Note the first argument: a column is namespaced by the application owning the **field it displays**, not the one owning the view. A field your app adds to a standard object gets its column derived under your application, on a view owned by Twenty.
<Warning>
System views and their columns are **server-owned**: resolve their identifiers
to reference them, never to declare them. `key` on
[`defineView()`](/developers/extend/apps/layout/views) is deprecated and
ignored, so a manifest view can never claim the `INDEX` key, and the server
already provisions a column for every field you add, so declaring your own
`defineViewField()` for that same field on a system view conflicts with it.
</Warning>
## Standard Twenty objects
For a **standard** Twenty object (Person, Company, Opportunity, …), you don't need to derive anything: the system field identifiers are pre-computed constants you can import directly.
For a **standard** Twenty object (Person, Company, Opportunity, …), you don't need to derive anything: the identifiers are pre-computed constants you can import directly, for both fields and views.
```ts
import { STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS } from 'twenty-sdk/define';
// STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.fields.createdAt.universalIdentifier
// STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.fields.updatedAt.universalIdentifier
// STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.views.allPeople.universalIdentifier
```
Reach for `getFieldUniversalIdentifier` when the object is one **your app** defines with [`defineObject()`](/developers/extend/apps/data/objects), where no such constant exists.
Reach for the helpers above when the object is one **your app** defines with [`defineObject()`](/developers/extend/apps/data/objects), where no such constant exists.
<Note>
`name` is a **default** field, not a system field. It keeps its own hardcoded

View File

@@ -35,7 +35,7 @@ export default defineView({
- `objectUniversalIdentifier` specifies which object this view applies to. It can be a custom object you defined or a standard Twenty object.
- The object's main list view is server-owned: `key` is deprecated and ignored, so a manifest view can never claim it. Ship a `VIEW` navigation item if you want your view one click away in the sidebar.
- `fields` controls which columns appear and in what order. Each field references a `fieldMetadataUniversalIdentifier`. To reference an auto-created system field such as `createdAt`, see [Targeting System Fields](/developers/extend/apps/data/system-fields).
- `fields` controls which columns appear and in what order. Each field references a `fieldMetadataUniversalIdentifier`. To reference an auto-created system field such as `createdAt`, see [Targeting System Metadata](/developers/extend/apps/data/system-fields).
- You can also declare `filters`, `filterGroups`, `sorts`, `groups`, and `fieldGroups` for advanced configurations.
- `position` controls ordering when multiple views exist for the same object.

View File

@@ -39,6 +39,8 @@ exports[`stub-twenty-sdk-define plugin > matches the recorded export partition 1
"featureFlags",
"getFieldUniversalIdentifier",
"getSystemRelationFieldUniversalIdentifier",
"getSystemViewFieldUniversalIdentifier",
"getSystemViewUniversalIdentifier",
"hasAnySoftDeleteFilterOnView",
"includes",
"includesEvery",

View File

@@ -165,6 +165,10 @@ export { defineSkill } from '@/sdk/define/skills/define-skill';
export { defineView } from '@/sdk/define/views/define-view';
export { defineViewField } from '@/sdk/define/view-fields/define-view-field';
export {
getSystemViewFieldUniversalIdentifier,
getSystemViewUniversalIdentifier,
} from 'twenty-shared/application';
export type { ViewConfig } from '@/sdk/define/views/view-config';
export { ViewKey } from '@/sdk/define/views/view-key';
export type {

View File

@@ -2,8 +2,8 @@ import { InjectRepository } from '@nestjs/typeorm';
import { Command } from 'nest-commander';
import {
getSystemViewFieldUniversalIdentifier,
getSystemViewUniversalIdentifier,
getViewFieldUniversalIdentifier,
} from 'twenty-shared/application';
import { ViewKey } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
@@ -34,7 +34,7 @@ type ReownUpdate = {
@Command({
name: 'upgrade:2-26:reconcile-index-view-universal-identifier',
description:
'Re-own the INDEX table views ("All {objectLabelPlural}", keyed on ViewKey.INDEX) of the twenty-standard and workspace-custom applications, and all their view fields, onto the engine convention: the view gets the name-free deterministic universal identifier (getSystemViewUniversalIdentifier, object identifier + INDEX key), each view field gets the derived getViewFieldUniversalIdentifier keyed on the application of the field it DISPLAYS — not the row attribution, which diverges when a user shows a hidden standard column and mints a workspace-custom view field on a standard field — so an app or user column on a standard INDEX view converges too, and both get isSystemSideEffect: true, as if provisioned by the metadata side-effect engine. INDEX views of other applications are handled by the demote-and-backfill command. Children reference the view by primary key, so the re-own is a lossless update.',
'Re-own the INDEX table views ("All {objectLabelPlural}", keyed on ViewKey.INDEX) of the twenty-standard and workspace-custom applications, and all their view fields, onto the engine convention: the view gets the name-free deterministic universal identifier (getSystemViewUniversalIdentifier, object identifier + INDEX key), each view field gets the derived getSystemViewFieldUniversalIdentifier keyed on the application of the field it DISPLAYS — not the row attribution, which diverges when a user shows a hidden standard column and mints a workspace-custom view field on a standard field — so an app or user column on a standard INDEX view converges too, and both get isSystemSideEffect: true, as if provisioned by the metadata side-effect engine. INDEX views of other applications are handled by the demote-and-backfill command. Children reference the view by primary key, so the re-own is a lossless update.',
})
export class ReconcileIndexViewUniversalIdentifierCommand extends ProvisionedWorkspaceCommandRunner {
constructor(
@@ -171,7 +171,7 @@ export class ReconcileIndexViewUniversalIdentifierCommand extends ProvisionedWor
}
const derivedViewUniversalIdentifier = getSystemViewUniversalIdentifier({
applicationUniversalIdentifier:
objectMetadataApplicationUniversalIdentifier:
flatObjectMetadata.applicationUniversalIdentifier,
objectUniversalIdentifier: flatObjectMetadata.universalIdentifier,
viewKey: ViewKey.INDEX,
@@ -261,15 +261,14 @@ export class ReconcileIndexViewUniversalIdentifierCommand extends ProvisionedWor
return undefined;
}
const derivedViewFieldUniversalIdentifier = getViewFieldUniversalIdentifier(
{
applicationUniversalIdentifier:
const derivedViewFieldUniversalIdentifier =
getSystemViewFieldUniversalIdentifier({
fieldMetadataApplicationUniversalIdentifier:
flatFieldMetadata.applicationUniversalIdentifier,
viewUniversalIdentifier: derivedViewUniversalIdentifier,
fieldMetadataUniversalIdentifier:
flatViewField.fieldMetadataUniversalIdentifier,
},
);
});
const update: ReownUpdate['update'] = {};

View File

@@ -2,8 +2,8 @@ import { InjectRepository } from '@nestjs/typeorm';
import { Command } from 'nest-commander';
import {
getSystemViewFieldUniversalIdentifier,
getSystemViewUniversalIdentifier,
getViewFieldUniversalIdentifier,
} from 'twenty-shared/application';
import { ViewKey } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
@@ -220,7 +220,7 @@ export class DemoteAndBackfillApplicationIndexViewCommand extends ProvisionedWor
}
const indexViewUniversalIdentifier = getSystemViewUniversalIdentifier({
applicationUniversalIdentifier:
objectMetadataApplicationUniversalIdentifier:
flatObjectMetadata.applicationUniversalIdentifier,
objectUniversalIdentifier: flatObjectMetadata.universalIdentifier,
viewKey: ViewKey.INDEX,
@@ -276,12 +276,14 @@ export class DemoteAndBackfillApplicationIndexViewCommand extends ProvisionedWor
const fieldApplicationUniversalIdentifier =
flatFieldMetadata.applicationUniversalIdentifier;
const viewFieldUniversalIdentifier = getViewFieldUniversalIdentifier({
applicationUniversalIdentifier: fieldApplicationUniversalIdentifier,
viewUniversalIdentifier: indexViewUniversalIdentifier,
fieldMetadataUniversalIdentifier:
flatFieldMetadata.universalIdentifier,
});
const viewFieldUniversalIdentifier =
getSystemViewFieldUniversalIdentifier({
fieldMetadataApplicationUniversalIdentifier:
fieldApplicationUniversalIdentifier,
viewUniversalIdentifier: indexViewUniversalIdentifier,
fieldMetadataUniversalIdentifier:
flatFieldMetadata.universalIdentifier,
});
// Already backfilled by a previous (partially failed) run.
if (

View File

@@ -1,6 +1,6 @@
import {
getSystemViewUniversalIdentifier,
getViewFieldUniversalIdentifier,
getSystemViewFieldUniversalIdentifier,
} from 'twenty-shared/application';
import { ViewKey } from 'twenty-shared/types';
@@ -27,13 +27,13 @@ const FIELD_UNIVERSAL_IDENTIFIER = '20202020-0000-4000-8000-0000000000cc';
const DERIVED_STANDARD_VIEW_UNIVERSAL_IDENTIFIER =
getSystemViewUniversalIdentifier({
applicationUniversalIdentifier: STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER,
objectMetadataApplicationUniversalIdentifier: STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER,
objectUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIER,
viewKey: ViewKey.INDEX,
});
const DERIVED_STANDARD_VIEW_FIELD_UNIVERSAL_IDENTIFIER =
getViewFieldUniversalIdentifier({
applicationUniversalIdentifier: STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER,
getSystemViewFieldUniversalIdentifier({
fieldMetadataApplicationUniversalIdentifier: STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER,
viewUniversalIdentifier: DERIVED_STANDARD_VIEW_UNIVERSAL_IDENTIFIER,
fieldMetadataUniversalIdentifier: FIELD_UNIVERSAL_IDENTIFIER,
});
@@ -248,7 +248,7 @@ describe('ReconcileIndexViewUniversalIdentifierCommand', () => {
it('re-owns a custom object INDEX view onto the workspace-custom derivation', async () => {
const derivedCustomViewUniversalIdentifier =
getSystemViewUniversalIdentifier({
applicationUniversalIdentifier: CUSTOM_APPLICATION_UNIVERSAL_IDENTIFIER,
objectMetadataApplicationUniversalIdentifier: CUSTOM_APPLICATION_UNIVERSAL_IDENTIFIER,
objectUniversalIdentifier: CUSTOM_OBJECT_UNIVERSAL_IDENTIFIER,
viewKey: ViewKey.INDEX,
});
@@ -367,8 +367,8 @@ describe('ReconcileIndexViewUniversalIdentifierCommand', () => {
// those must converge on the derived scheme so manifest deletion inference
// never drops them once the app stops declaring them.
const derivedExternalViewFieldUniversalIdentifier =
getViewFieldUniversalIdentifier({
applicationUniversalIdentifier:
getSystemViewFieldUniversalIdentifier({
fieldMetadataApplicationUniversalIdentifier:
EXTERNAL_APPLICATION_UNIVERSAL_IDENTIFIER,
viewUniversalIdentifier: DERIVED_STANDARD_VIEW_UNIVERSAL_IDENTIFIER,
fieldMetadataUniversalIdentifier: FIELD_UNIVERSAL_IDENTIFIER,

View File

@@ -1,6 +1,6 @@
import {
getSystemViewUniversalIdentifier,
getViewFieldUniversalIdentifier,
getSystemViewFieldUniversalIdentifier,
} from 'twenty-shared/application';
import { FieldMetadataType, ViewKey } from 'twenty-shared/types';
import { In } from 'typeorm';
@@ -23,7 +23,7 @@ const OBJECT_UNIVERSAL_IDENTIFIER = '20202020-0000-4000-8000-0000000000bb';
const FIELD_UNIVERSAL_IDENTIFIER = '20202020-0000-4000-8000-0000000000cc';
const DERIVED_VIEW_UNIVERSAL_IDENTIFIER = getSystemViewUniversalIdentifier({
applicationUniversalIdentifier: EXTERNAL_APPLICATION_UNIVERSAL_IDENTIFIER,
objectMetadataApplicationUniversalIdentifier: EXTERNAL_APPLICATION_UNIVERSAL_IDENTIFIER,
objectUniversalIdentifier: OBJECT_UNIVERSAL_IDENTIFIER,
viewKey: ViewKey.INDEX,
});
@@ -205,8 +205,8 @@ describe('DemoteAndBackfillApplicationIndexViewCommand', () => {
.flatEntityToCreate,
).toEqual([
expect.objectContaining({
universalIdentifier: getViewFieldUniversalIdentifier({
applicationUniversalIdentifier:
universalIdentifier: getSystemViewFieldUniversalIdentifier({
fieldMetadataApplicationUniversalIdentifier:
EXTERNAL_APPLICATION_UNIVERSAL_IDENTIFIER,
viewUniversalIdentifier: DERIVED_VIEW_UNIVERSAL_IDENTIFIER,
fieldMetadataUniversalIdentifier: FIELD_UNIVERSAL_IDENTIFIER,
@@ -241,8 +241,8 @@ describe('DemoteAndBackfillApplicationIndexViewCommand', () => {
],
viewFields: [
{
universalIdentifier: getViewFieldUniversalIdentifier({
applicationUniversalIdentifier:
universalIdentifier: getSystemViewFieldUniversalIdentifier({
fieldMetadataApplicationUniversalIdentifier:
EXTERNAL_APPLICATION_UNIVERSAL_IDENTIFIER,
viewUniversalIdentifier: DERIVED_VIEW_UNIVERSAL_IDENTIFIER,
fieldMetadataUniversalIdentifier: FIELD_UNIVERSAL_IDENTIFIER,
@@ -282,8 +282,8 @@ describe('DemoteAndBackfillApplicationIndexViewCommand', () => {
payload.allFlatEntityOperationByMetadataName.viewField.flatEntityToCreate,
).toEqual([
expect.objectContaining({
universalIdentifier: getViewFieldUniversalIdentifier({
applicationUniversalIdentifier:
universalIdentifier: getSystemViewFieldUniversalIdentifier({
fieldMetadataApplicationUniversalIdentifier:
EXTERNAL_APPLICATION_UNIVERSAL_IDENTIFIER,
viewUniversalIdentifier: DERIVED_VIEW_UNIVERSAL_IDENTIFIER,
fieldMetadataUniversalIdentifier: FIELD_UNIVERSAL_IDENTIFIER,

View File

@@ -1,7 +1,7 @@
import {
getFieldUniversalIdentifier,
getSystemViewFieldUniversalIdentifier,
getSystemViewUniversalIdentifier,
getViewFieldUniversalIdentifier,
} from 'twenty-shared/application';
import { FieldMetadataType, ViewKey } from 'twenty-shared/types';
@@ -25,7 +25,8 @@ const PRIORITY_FIELD_UNIVERSAL_IDENTIFIER =
const DERIVED_INDEX_VIEW_UNIVERSAL_IDENTIFIER =
getSystemViewUniversalIdentifier({
applicationUniversalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER,
objectMetadataApplicationUniversalIdentifier:
APPLICATION_UNIVERSAL_IDENTIFIER,
objectUniversalIdentifier: OBJECT_UNIVERSAL_IDENTIFIER,
viewKey: ViewKey.INDEX,
});
@@ -37,8 +38,9 @@ const computeViewFieldUniversalIdentifier = ({
viewUniversalIdentifier: string;
fieldMetadataUniversalIdentifier: string;
}) =>
getViewFieldUniversalIdentifier({
applicationUniversalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER,
getSystemViewFieldUniversalIdentifier({
fieldMetadataApplicationUniversalIdentifier:
APPLICATION_UNIVERSAL_IDENTIFIER,
viewUniversalIdentifier,
fieldMetadataUniversalIdentifier,
});

View File

@@ -1,8 +1,8 @@
import { Injectable } from '@nestjs/common';
import {
getSystemViewFieldUniversalIdentifier,
getSystemViewUniversalIdentifier,
getViewFieldUniversalIdentifier,
} from 'twenty-shared/application';
import { ViewKey } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
@@ -56,7 +56,7 @@ export class FieldIndexViewFieldOnCreateSideEffectHandlerService extends Metadat
}
const indexViewUniversalIdentifier = getSystemViewUniversalIdentifier({
applicationUniversalIdentifier:
objectMetadataApplicationUniversalIdentifier:
parentFlatObjectMetadata.applicationUniversalIdentifier,
objectUniversalIdentifier: objectMetadataUniversalIdentifier,
viewKey: ViewKey.INDEX,
@@ -255,8 +255,9 @@ export class FieldIndexViewFieldOnCreateSideEffectHandlerService extends Metadat
const { applicationUniversalIdentifier } = sourceFlatFieldMetadata;
return {
universalIdentifier: getViewFieldUniversalIdentifier({
applicationUniversalIdentifier,
universalIdentifier: getSystemViewFieldUniversalIdentifier({
fieldMetadataApplicationUniversalIdentifier:
applicationUniversalIdentifier,
viewUniversalIdentifier: indexViewUniversalIdentifier,
fieldMetadataUniversalIdentifier:
sourceFlatFieldMetadata.universalIdentifier,

View File

@@ -10,7 +10,8 @@ const NAME_FIELD_UNIVERSAL_IDENTIFIER = 'd1d2d3d4-d5d6-4000-8000-000000000001';
const CODE_FIELD_UNIVERSAL_IDENTIFIER = 'd1d2d3d4-d5d6-4000-8000-000000000002';
const INDEX_VIEW_UNIVERSAL_IDENTIFIER = getSystemViewUniversalIdentifier({
applicationUniversalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER,
objectMetadataApplicationUniversalIdentifier:
APPLICATION_UNIVERSAL_IDENTIFIER,
objectUniversalIdentifier: OBJECT_UNIVERSAL_IDENTIFIER,
viewKey: ViewKey.INDEX,
});

View File

@@ -1,7 +1,7 @@
import {
getFieldUniversalIdentifier,
getSystemViewFieldUniversalIdentifier,
getSystemViewUniversalIdentifier,
getViewFieldUniversalIdentifier,
} from 'twenty-shared/application';
import { FieldMetadataType, ViewKey } from 'twenty-shared/types';
@@ -46,7 +46,8 @@ const DISPLAYABLE_SYSTEM_FIELD_NAMES = [
const DERIVED_INDEX_VIEW_UNIVERSAL_IDENTIFIER =
getSystemViewUniversalIdentifier({
applicationUniversalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER,
objectMetadataApplicationUniversalIdentifier:
APPLICATION_UNIVERSAL_IDENTIFIER,
objectUniversalIdentifier: OBJECT_UNIVERSAL_IDENTIFIER,
viewKey: ViewKey.INDEX,
});
@@ -192,8 +193,9 @@ describe('ObjectSystemFieldsAndIndexViewOnCreateSideEffectHandlerService', () =>
for (const viewField of viewFields) {
expect(viewField.isSystemSideEffect).toBe(true);
expect(viewField.universalIdentifier).toBe(
getViewFieldUniversalIdentifier({
applicationUniversalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER,
getSystemViewFieldUniversalIdentifier({
fieldMetadataApplicationUniversalIdentifier:
APPLICATION_UNIVERSAL_IDENTIFIER,
viewUniversalIdentifier: DERIVED_INDEX_VIEW_UNIVERSAL_IDENTIFIER,
fieldMetadataUniversalIdentifier:
viewField.fieldMetadataUniversalIdentifier,

View File

@@ -66,7 +66,7 @@ export class ObjectIndexViewLabelIdentifierOnUpdateSideEffectHandlerService exte
}
const indexViewUniversalIdentifier = getSystemViewUniversalIdentifier({
applicationUniversalIdentifier:
objectMetadataApplicationUniversalIdentifier:
updatedFlatObjectMetadata.applicationUniversalIdentifier,
objectUniversalIdentifier: updatedFlatObjectMetadata.universalIdentifier,
viewKey: ViewKey.INDEX,

View File

@@ -15,7 +15,8 @@ describe('computeFlatIndexViewToCreate', () => {
expect(result.universalIdentifier).toBe(
getSystemViewUniversalIdentifier({
applicationUniversalIdentifier,
objectMetadataApplicationUniversalIdentifier:
applicationUniversalIdentifier,
objectUniversalIdentifier,
viewKey: ViewKey.INDEX,
}),

View File

@@ -43,7 +43,8 @@ export const computeFlatIndexViewToCreate = ({
openRecordIn: ViewOpenRecordIn.SIDE_PANEL,
position: 0,
universalIdentifier: getSystemViewUniversalIdentifier({
applicationUniversalIdentifier,
objectMetadataApplicationUniversalIdentifier:
applicationUniversalIdentifier,
objectUniversalIdentifier: objectMetadata.universalIdentifier,
viewKey: ViewKey.INDEX,
}),

View File

@@ -0,0 +1,34 @@
import { getSystemViewFieldUniversalIdentifier } from '@/application/deterministic-identifier/get-system-view-field-universal-identifier.util';
import { getViewFieldUniversalIdentifier } from '@/application/deterministic-identifier/get-view-field-universal-identifier.util';
const APP = '11111111-1111-4111-8111-111111111111';
const VIEW = '44444444-4444-4444-8444-444444444444';
const FIELD = '33333333-3333-4333-8333-333333333333';
describe('getSystemViewFieldUniversalIdentifier', () => {
it('derives a deterministic id from the field it displays within its view', () => {
expect(
getSystemViewFieldUniversalIdentifier({
fieldMetadataApplicationUniversalIdentifier: APP,
viewUniversalIdentifier: VIEW,
fieldMetadataUniversalIdentifier: FIELD,
}),
).toBe('fe533473-ef93-5ed9-b709-b344aa94fb8d');
});
it('derives the same id as getViewFieldUniversalIdentifier', () => {
expect(
getSystemViewFieldUniversalIdentifier({
fieldMetadataApplicationUniversalIdentifier: APP,
viewUniversalIdentifier: VIEW,
fieldMetadataUniversalIdentifier: FIELD,
}),
).toBe(
getViewFieldUniversalIdentifier({
applicationUniversalIdentifier: APP,
viewUniversalIdentifier: VIEW,
fieldMetadataUniversalIdentifier: FIELD,
}),
);
});
});

View File

@@ -8,7 +8,7 @@ describe('getSystemViewUniversalIdentifier', () => {
it('derives a deterministic id from the stable INDEX view key within its object', () => {
expect(
getSystemViewUniversalIdentifier({
applicationUniversalIdentifier: APP,
objectMetadataApplicationUniversalIdentifier: APP,
objectUniversalIdentifier: OBJECT,
viewKey: ViewKey.INDEX,
}),

View File

@@ -0,0 +1,16 @@
import { computeDeterministicUuid } from '@/application/deterministic-identifier/compute-deterministic-uuid.util';
export const getSystemViewFieldUniversalIdentifier = ({
fieldMetadataApplicationUniversalIdentifier,
viewUniversalIdentifier,
fieldMetadataUniversalIdentifier,
}: {
fieldMetadataApplicationUniversalIdentifier: string;
viewUniversalIdentifier: string;
fieldMetadataUniversalIdentifier: string;
}): string =>
computeDeterministicUuid({
entityNamespace: 'viewField',
value: `${viewUniversalIdentifier}:${fieldMetadataUniversalIdentifier}`,
applicationUniversalIdentifier: fieldMetadataApplicationUniversalIdentifier,
});

View File

@@ -2,16 +2,17 @@ import { computeDeterministicUuid } from '@/application/deterministic-identifier
import { type ViewKey } from '@/types/ViewKey';
export const getSystemViewUniversalIdentifier = ({
applicationUniversalIdentifier,
objectMetadataApplicationUniversalIdentifier,
objectUniversalIdentifier,
viewKey,
}: {
applicationUniversalIdentifier: string;
objectMetadataApplicationUniversalIdentifier: string;
objectUniversalIdentifier: string;
viewKey: ViewKey;
}): string =>
computeDeterministicUuid({
entityNamespace: 'view',
value: `${objectUniversalIdentifier}:${viewKey}`,
applicationUniversalIdentifier,
applicationUniversalIdentifier:
objectMetadataApplicationUniversalIdentifier,
});

View File

@@ -80,6 +80,7 @@ export { getSearchFieldUniversalIdentifier } from './deterministic-identifier/ge
export { getSelectOptionUniversalIdentifier } from './deterministic-identifier/get-select-option-universal-identifier.util';
export { getSkillUniversalIdentifier } from './deterministic-identifier/get-skill-universal-identifier.util';
export { getSystemRelationFieldUniversalIdentifier } from './deterministic-identifier/get-system-relation-field-universal-identifier.util';
export { getSystemViewFieldUniversalIdentifier } from './deterministic-identifier/get-system-view-field-universal-identifier.util';
export { getSystemViewUniversalIdentifier } from './deterministic-identifier/get-system-view-universal-identifier.util';
export { getViewFieldGroupUniversalIdentifier } from './deterministic-identifier/get-view-field-group-universal-identifier.util';
export { getViewFieldUniversalIdentifier } from './deterministic-identifier/get-view-field-universal-identifier.util';

View File

@@ -12,7 +12,7 @@ import { buildStandardObjectIndexView } from '@/metadata/utils/internal/build-st
// keyed on ViewKey.INDEX) and their view-field universal identifiers are
// deterministically derived by buildStandardObjectIndexView
// (getSystemViewUniversalIdentifier for the view,
// getViewFieldUniversalIdentifier for each view field).
// getSystemViewFieldUniversalIdentifier for each view field).
export const STANDARD_OBJECTS = {
attachment: {
universalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.attachment,

View File

@@ -1,6 +1,6 @@
import { TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER } from '@/application/constants/TwentyStandardApplicationUniversalIdentifier';
import { getSystemViewFieldUniversalIdentifier } from '@/application/deterministic-identifier/get-system-view-field-universal-identifier.util';
import { getSystemViewUniversalIdentifier } from '@/application/deterministic-identifier/get-system-view-universal-identifier.util';
import { getViewFieldUniversalIdentifier } from '@/application/deterministic-identifier/get-view-field-universal-identifier.util';
import { ViewKey } from '@/types/ViewKey';
type StandardViewFieldUniversalIdentifier = { universalIdentifier: string };
@@ -20,7 +20,7 @@ export const buildStandardObjectIndexView = <
viewFields: Record<TViewFieldName, StandardViewFieldUniversalIdentifier>;
} => {
const viewUniversalIdentifier = getSystemViewUniversalIdentifier({
applicationUniversalIdentifier:
objectMetadataApplicationUniversalIdentifier:
TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER,
objectUniversalIdentifier,
viewKey: ViewKey.INDEX,
@@ -39,8 +39,8 @@ export const buildStandardObjectIndexView = <
return [
viewFieldName,
{
universalIdentifier: getViewFieldUniversalIdentifier({
applicationUniversalIdentifier:
universalIdentifier: getSystemViewFieldUniversalIdentifier({
fieldMetadataApplicationUniversalIdentifier:
TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER,
viewUniversalIdentifier,
fieldMetadataUniversalIdentifier: field.universalIdentifier,