mirror of
https://github.com/twentyhq/twenty.git
synced 2026-08-02 18:54:43 -04:00
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:
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -39,6 +39,8 @@ exports[`stub-twenty-sdk-define plugin > matches the recorded export partition 1
|
||||
"featureFlags",
|
||||
"getFieldUniversalIdentifier",
|
||||
"getSystemRelationFieldUniversalIdentifier",
|
||||
"getSystemViewFieldUniversalIdentifier",
|
||||
"getSystemViewUniversalIdentifier",
|
||||
"hasAnySoftDeleteFilterOnView",
|
||||
"includes",
|
||||
"includesEvery",
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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'] = {};
|
||||
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -66,7 +66,7 @@ export class ObjectIndexViewLabelIdentifierOnUpdateSideEffectHandlerService exte
|
||||
}
|
||||
|
||||
const indexViewUniversalIdentifier = getSystemViewUniversalIdentifier({
|
||||
applicationUniversalIdentifier:
|
||||
objectMetadataApplicationUniversalIdentifier:
|
||||
updatedFlatObjectMetadata.applicationUniversalIdentifier,
|
||||
objectUniversalIdentifier: updatedFlatObjectMetadata.universalIdentifier,
|
||||
viewKey: ViewKey.INDEX,
|
||||
|
||||
@@ -15,7 +15,8 @@ describe('computeFlatIndexViewToCreate', () => {
|
||||
|
||||
expect(result.universalIdentifier).toBe(
|
||||
getSystemViewUniversalIdentifier({
|
||||
applicationUniversalIdentifier,
|
||||
objectMetadataApplicationUniversalIdentifier:
|
||||
applicationUniversalIdentifier,
|
||||
objectUniversalIdentifier,
|
||||
viewKey: ViewKey.INDEX,
|
||||
}),
|
||||
|
||||
@@ -43,7 +43,8 @@ export const computeFlatIndexViewToCreate = ({
|
||||
openRecordIn: ViewOpenRecordIn.SIDE_PANEL,
|
||||
position: 0,
|
||||
universalIdentifier: getSystemViewUniversalIdentifier({
|
||||
applicationUniversalIdentifier,
|
||||
objectMetadataApplicationUniversalIdentifier:
|
||||
applicationUniversalIdentifier,
|
||||
objectUniversalIdentifier: objectMetadata.universalIdentifier,
|
||||
viewKey: ViewKey.INDEX,
|
||||
}),
|
||||
|
||||
@@ -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,
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -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,
|
||||
}),
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user