diff --git a/packages/twenty-docs/developers/extend/apps/data/system-fields.mdx b/packages/twenty-docs/developers/extend/apps/data/system-fields.mdx
index ce654a9d12b..57a3b81cf87 100644
--- a/packages/twenty-docs/developers/extend/apps/data/system-fields.mdx
+++ b/packages/twenty-docs/developers/extend/apps/data/system-fields.mdx
@@ -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
`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.
-## System relation fields
+### System relation fields
`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
+
+
+ `getSystemViewUniversalIdentifier` and `getSystemViewFieldUniversalIdentifier`
+ are available from `twenty-sdk` 2.26 onward and require a Twenty server on
+ 2.26 or later.
+
+
+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.
+
+
+ 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.
+
+
## 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.
`name` is a **default** field, not a system field. It keeps its own hardcoded
diff --git a/packages/twenty-docs/developers/extend/apps/layout/views.mdx b/packages/twenty-docs/developers/extend/apps/layout/views.mdx
index 7fe0609737f..8ea606099b7 100644
--- a/packages/twenty-docs/developers/extend/apps/layout/views.mdx
+++ b/packages/twenty-docs/developers/extend/apps/layout/views.mdx
@@ -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.
diff --git a/packages/twenty-sdk/src/cli/utilities/build/common/plugins/__tests__/__snapshots__/stub-twenty-sdk-define.plugin.spec.ts.snap b/packages/twenty-sdk/src/cli/utilities/build/common/plugins/__tests__/__snapshots__/stub-twenty-sdk-define.plugin.spec.ts.snap
index 0c7f2dd5942..ebd1f317ee7 100644
--- a/packages/twenty-sdk/src/cli/utilities/build/common/plugins/__tests__/__snapshots__/stub-twenty-sdk-define.plugin.spec.ts.snap
+++ b/packages/twenty-sdk/src/cli/utilities/build/common/plugins/__tests__/__snapshots__/stub-twenty-sdk-define.plugin.spec.ts.snap
@@ -39,6 +39,8 @@ exports[`stub-twenty-sdk-define plugin > matches the recorded export partition 1
"featureFlags",
"getFieldUniversalIdentifier",
"getSystemRelationFieldUniversalIdentifier",
+ "getSystemViewFieldUniversalIdentifier",
+ "getSystemViewUniversalIdentifier",
"hasAnySoftDeleteFilterOnView",
"includes",
"includesEvery",
diff --git a/packages/twenty-sdk/src/sdk/define/index.ts b/packages/twenty-sdk/src/sdk/define/index.ts
index 88be5b61520..d696eada880 100644
--- a/packages/twenty-sdk/src/sdk/define/index.ts
+++ b/packages/twenty-sdk/src/sdk/define/index.ts
@@ -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 {
diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/2-26/2-26-workspace-command-1785255689000-reconcile-index-view-universal-identifier.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/2-26/2-26-workspace-command-1785255689000-reconcile-index-view-universal-identifier.command.ts
index 2fb9e1e8ebb..70a0bd5b015 100644
--- a/packages/twenty-server/src/database/commands/upgrade-version-command/2-26/2-26-workspace-command-1785255689000-reconcile-index-view-universal-identifier.command.ts
+++ b/packages/twenty-server/src/database/commands/upgrade-version-command/2-26/2-26-workspace-command-1785255689000-reconcile-index-view-universal-identifier.command.ts
@@ -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'] = {};
diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/2-26/2-26-workspace-command-1785255690000-demote-and-backfill-application-index-view.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/2-26/2-26-workspace-command-1785255690000-demote-and-backfill-application-index-view.command.ts
index c5ce6d86aa0..3cd402d9f20 100644
--- a/packages/twenty-server/src/database/commands/upgrade-version-command/2-26/2-26-workspace-command-1785255690000-demote-and-backfill-application-index-view.command.ts
+++ b/packages/twenty-server/src/database/commands/upgrade-version-command/2-26/2-26-workspace-command-1785255690000-demote-and-backfill-application-index-view.command.ts
@@ -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 (
diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/2-26/__tests__/2-26-workspace-command-1785255689000-reconcile-index-view-universal-identifier.command.spec.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/2-26/__tests__/2-26-workspace-command-1785255689000-reconcile-index-view-universal-identifier.command.spec.ts
index 9a047355e4b..07f5ed428a9 100644
--- a/packages/twenty-server/src/database/commands/upgrade-version-command/2-26/__tests__/2-26-workspace-command-1785255689000-reconcile-index-view-universal-identifier.command.spec.ts
+++ b/packages/twenty-server/src/database/commands/upgrade-version-command/2-26/__tests__/2-26-workspace-command-1785255689000-reconcile-index-view-universal-identifier.command.spec.ts
@@ -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,
diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/2-26/__tests__/2-26-workspace-command-1785255690000-demote-and-backfill-application-index-view.command.spec.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/2-26/__tests__/2-26-workspace-command-1785255690000-demote-and-backfill-application-index-view.command.spec.ts
index 1ee3df5a0b0..713cabe3924 100644
--- a/packages/twenty-server/src/database/commands/upgrade-version-command/2-26/__tests__/2-26-workspace-command-1785255690000-demote-and-backfill-application-index-view.command.spec.ts
+++ b/packages/twenty-server/src/database/commands/upgrade-version-command/2-26/__tests__/2-26-workspace-command-1785255690000-demote-and-backfill-application-index-view.command.spec.ts
@@ -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,
diff --git a/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/field-metadata/services/__tests__/field-index-view-field-on-create-side-effect-handler.service.spec.ts b/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/field-metadata/services/__tests__/field-index-view-field-on-create-side-effect-handler.service.spec.ts
index 95cb8b0f18b..3faf582ee4a 100644
--- a/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/field-metadata/services/__tests__/field-index-view-field-on-create-side-effect-handler.service.spec.ts
+++ b/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/field-metadata/services/__tests__/field-index-view-field-on-create-side-effect-handler.service.spec.ts
@@ -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,
});
diff --git a/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/field-metadata/services/field-index-view-field-on-create-side-effect-handler.service.ts b/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/field-metadata/services/field-index-view-field-on-create-side-effect-handler.service.ts
index 837cce885fe..f23c8c1f54c 100644
--- a/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/field-metadata/services/field-index-view-field-on-create-side-effect-handler.service.ts
+++ b/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/field-metadata/services/field-index-view-field-on-create-side-effect-handler.service.ts
@@ -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,
diff --git a/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/object-metadata/services/__tests__/object-index-view-label-identifier-on-update-side-effect-handler.service.spec.ts b/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/object-metadata/services/__tests__/object-index-view-label-identifier-on-update-side-effect-handler.service.spec.ts
index 42199407433..cd3e60f93c1 100644
--- a/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/object-metadata/services/__tests__/object-index-view-label-identifier-on-update-side-effect-handler.service.spec.ts
+++ b/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/object-metadata/services/__tests__/object-index-view-label-identifier-on-update-side-effect-handler.service.spec.ts
@@ -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,
});
diff --git a/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/object-metadata/services/__tests__/object-system-fields-and-index-view-on-create-side-effect-handler.service.spec.ts b/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/object-metadata/services/__tests__/object-system-fields-and-index-view-on-create-side-effect-handler.service.spec.ts
index 35eece996b3..31316e111ab 100644
--- a/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/object-metadata/services/__tests__/object-system-fields-and-index-view-on-create-side-effect-handler.service.spec.ts
+++ b/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/object-metadata/services/__tests__/object-system-fields-and-index-view-on-create-side-effect-handler.service.spec.ts
@@ -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,
diff --git a/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/object-metadata/services/object-index-view-label-identifier-on-update-side-effect-handler.service.ts b/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/object-metadata/services/object-index-view-label-identifier-on-update-side-effect-handler.service.ts
index 47a54708a2b..23a70ebb2ea 100644
--- a/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/object-metadata/services/object-index-view-label-identifier-on-update-side-effect-handler.service.ts
+++ b/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/object-metadata/services/object-index-view-label-identifier-on-update-side-effect-handler.service.ts
@@ -66,7 +66,7 @@ export class ObjectIndexViewLabelIdentifierOnUpdateSideEffectHandlerService exte
}
const indexViewUniversalIdentifier = getSystemViewUniversalIdentifier({
- applicationUniversalIdentifier:
+ objectMetadataApplicationUniversalIdentifier:
updatedFlatObjectMetadata.applicationUniversalIdentifier,
objectUniversalIdentifier: updatedFlatObjectMetadata.universalIdentifier,
viewKey: ViewKey.INDEX,
diff --git a/packages/twenty-server/src/engine/metadata-modules/object-metadata/utils/__tests__/compute-flat-index-view-to-create.util.spec.ts b/packages/twenty-server/src/engine/metadata-modules/object-metadata/utils/__tests__/compute-flat-index-view-to-create.util.spec.ts
index 84f1cd39ab1..80c3446c0e5 100644
--- a/packages/twenty-server/src/engine/metadata-modules/object-metadata/utils/__tests__/compute-flat-index-view-to-create.util.spec.ts
+++ b/packages/twenty-server/src/engine/metadata-modules/object-metadata/utils/__tests__/compute-flat-index-view-to-create.util.spec.ts
@@ -15,7 +15,8 @@ describe('computeFlatIndexViewToCreate', () => {
expect(result.universalIdentifier).toBe(
getSystemViewUniversalIdentifier({
- applicationUniversalIdentifier,
+ objectMetadataApplicationUniversalIdentifier:
+ applicationUniversalIdentifier,
objectUniversalIdentifier,
viewKey: ViewKey.INDEX,
}),
diff --git a/packages/twenty-server/src/engine/metadata-modules/object-metadata/utils/compute-flat-index-view-to-create.util.ts b/packages/twenty-server/src/engine/metadata-modules/object-metadata/utils/compute-flat-index-view-to-create.util.ts
index eb234b914b9..1421eeb635e 100644
--- a/packages/twenty-server/src/engine/metadata-modules/object-metadata/utils/compute-flat-index-view-to-create.util.ts
+++ b/packages/twenty-server/src/engine/metadata-modules/object-metadata/utils/compute-flat-index-view-to-create.util.ts
@@ -43,7 +43,8 @@ export const computeFlatIndexViewToCreate = ({
openRecordIn: ViewOpenRecordIn.SIDE_PANEL,
position: 0,
universalIdentifier: getSystemViewUniversalIdentifier({
- applicationUniversalIdentifier,
+ objectMetadataApplicationUniversalIdentifier:
+ applicationUniversalIdentifier,
objectUniversalIdentifier: objectMetadata.universalIdentifier,
viewKey: ViewKey.INDEX,
}),
diff --git a/packages/twenty-shared/src/application/deterministic-identifier/__tests__/get-system-view-field-universal-identifier.util.spec.ts b/packages/twenty-shared/src/application/deterministic-identifier/__tests__/get-system-view-field-universal-identifier.util.spec.ts
new file mode 100644
index 00000000000..8b1a672d443
--- /dev/null
+++ b/packages/twenty-shared/src/application/deterministic-identifier/__tests__/get-system-view-field-universal-identifier.util.spec.ts
@@ -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,
+ }),
+ );
+ });
+});
diff --git a/packages/twenty-shared/src/application/deterministic-identifier/__tests__/get-system-view-universal-identifier.util.spec.ts b/packages/twenty-shared/src/application/deterministic-identifier/__tests__/get-system-view-universal-identifier.util.spec.ts
index a740c2c2f40..3930f3ca881 100644
--- a/packages/twenty-shared/src/application/deterministic-identifier/__tests__/get-system-view-universal-identifier.util.spec.ts
+++ b/packages/twenty-shared/src/application/deterministic-identifier/__tests__/get-system-view-universal-identifier.util.spec.ts
@@ -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,
}),
diff --git a/packages/twenty-shared/src/application/deterministic-identifier/get-system-view-field-universal-identifier.util.ts b/packages/twenty-shared/src/application/deterministic-identifier/get-system-view-field-universal-identifier.util.ts
new file mode 100644
index 00000000000..5bf7763bce3
--- /dev/null
+++ b/packages/twenty-shared/src/application/deterministic-identifier/get-system-view-field-universal-identifier.util.ts
@@ -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,
+ });
diff --git a/packages/twenty-shared/src/application/deterministic-identifier/get-system-view-universal-identifier.util.ts b/packages/twenty-shared/src/application/deterministic-identifier/get-system-view-universal-identifier.util.ts
index 51b88da6664..1f18f57789c 100644
--- a/packages/twenty-shared/src/application/deterministic-identifier/get-system-view-universal-identifier.util.ts
+++ b/packages/twenty-shared/src/application/deterministic-identifier/get-system-view-universal-identifier.util.ts
@@ -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,
});
diff --git a/packages/twenty-shared/src/application/index.ts b/packages/twenty-shared/src/application/index.ts
index 245432fc625..613c04218a7 100644
--- a/packages/twenty-shared/src/application/index.ts
+++ b/packages/twenty-shared/src/application/index.ts
@@ -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';
diff --git a/packages/twenty-shared/src/metadata/constants/standard-object.constant.ts b/packages/twenty-shared/src/metadata/constants/standard-object.constant.ts
index 90d9f8063d2..00908600d2b 100644
--- a/packages/twenty-shared/src/metadata/constants/standard-object.constant.ts
+++ b/packages/twenty-shared/src/metadata/constants/standard-object.constant.ts
@@ -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,
diff --git a/packages/twenty-shared/src/metadata/utils/internal/build-standard-object-index-view.util.ts b/packages/twenty-shared/src/metadata/utils/internal/build-standard-object-index-view.util.ts
index 4fb6e3a1e00..04e3db24639 100644
--- a/packages/twenty-shared/src/metadata/utils/internal/build-standard-object-index-view.util.ts
+++ b/packages/twenty-shared/src/metadata/utils/internal/build-standard-object-index-view.util.ts
@@ -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;
} => {
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,