From 1be5a0e54a35922afa8096adf872c3aafcb94e1c Mon Sep 17 00:00:00 2001 From: Paul Rastoin <45004772+prastoin@users.noreply.github.com> Date: Mon, 20 Jul 2026 18:53:24 +0200 Subject: [PATCH] System side effect relations (#22882) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes twentyhq/core-team-issues#2667 ## What Default relations to the standard relation objects (`timelineActivities`, `attachments`, `noteTargets`, `taskTargets`) are now fully owned by the **metadata side-effect engine**. Neither the API transpilers nor the SDK manifest builder provision them anymore: any object creation, rename or deletion — regardless of the caller — goes through the same engine handlers. ## Why - Provisioning was duplicated across the API path and the SDK manifest builder, with diverging behavior. - Universal identifiers of relation fields were derived from object **names**, so renaming an object mutated them and forced lossy delete+create cycles on manifest sync. ## How ### Engine-owned lifecycle (side-effect handlers) - `objectSystemRelationsOnCreate`: provisions the 8 forward/reverse relation fields (+ join column indexes) when an object is created. - `objectSystemRelationsOnUpdate`: renames the reverse morph fields (`target`) when their host object is renamed — a lossless `fieldMetadata.update`. - `objectSystemSideEffectsOnDelete`: cascades deletion of engine-owned fields/indexes when the object is deleted. - The API transpilers and the SDK `buildManifest` no longer inject these fields; `isSystemSideEffect: true` marks engine-owned entities, guarded by a granular property allowlist (only `isActive` is user-editable) and excluded from manifest deletion inference. ### Name-free deterministic universal identifiers New `getSystemRelationFieldUniversalIdentifier({ applicationUniversalIdentifier, objectUniversalIdentifier, relationTargetObjectUniversalIdentifier })` in `twenty-shared`, exported from `twenty-sdk/define`. The identifier is keyed on the two **object** identifiers instead of field names (direction encoded by argument order), so object renames never mutate relation field identifiers. It cannot collide with the name-based `getFieldUniversalIdentifier` derivation (field names cannot contain `:`). ### twenty-standard re-owned All 48 forward/reverse system relation field declarations in `STANDARD_OBJECTS` now pin the derived name-free identifiers (computed inline via the shared util) and carry `isSystemSideEffect: true`, with labels/icons declared explicitly (translated via `msg`). `twenty-standard` is projected as if the engine had generated these fields itself. ### 2.23 upgrade commands - `reconcile-system-relation-field-universal-identifier`: structurally matches existing default relation fields per workspace and backfills the derived universal identifiers, `isSystemSideEffect` flags, and standard labels/icons. - `upgrade-people-data-labs-application`: upgrades installed PDL apps to `1.0.7` right after the backfill to close the desync window (its views reference the re-derived identifiers). ### Misc - `people-data-labs` `1.0.7`: views temporarily pin the new derived identifiers (TODO: import from the next released `twenty-sdk`). - `UpgradeStatusModule` split out of `UpgradeModule` so the application module cluster can consume upgrade status/migration services without importing the versioned command bundles (fixes a require cycle that crashed boot). - Docs: `system-fields.mdx` documents the system relation fields and their resolver; `sync-and-recovery.mdx` plan example no longer shows auto-injected relations. ## Known red CI `people-data-labs (dockerhub-latest)` fails by design until the 2.23 server image is published: the app pins the new identifiers which only exist on a 2.23 server. The `local` leg (server built from this branch) is green. ## System fields are no longer manifest-authorable (accepted regression) The manifest converter no longer derives `isSystem` / `isSystemSideEffect` from field names. Reserved-system-named manifest fields (`id`, `createdAt`, `updatedAt`, `deletedAt`, `createdBy`, `updatedBy`, `position`, `searchVector`) are now skipped at conversion time when they carry the exact derived universal identifier (keeps manifests built with older SDKs installable), and rejected with `INVALID_INPUT` when they pin any other identifier. System fields are therefore fully engine-canonical: nothing a manifest carries can produce a system-flagged entity anymore. **Accepted regression**: a manifest can no longer influence system field properties at all. Previously a (legacy) re-declaration could shape them at creation — which actually produced broken system fields, e.g. a nullable, non-unique `id` — and could still toggle the allowlisted `isActive` / `universalSettings` afterwards. We consider this acceptable for now: per-app granularity over system fields will be reintroduced later through the **override framework**, which will also settle update semantics by forbidding direct updates over `isSystemSideEffect: true` entities and expressing divergence as overrides. `isSystemSideEffect`-only entities (the default relation fields provisioned by this PR) still have no engine-level update guard (see Follow-up below); that part is unchanged and also lands with the overrides refactor. ## Follow-up `isSystemSideEffect` field update/delete guards intentionally live at the API layer (`sanitize-raw-update-field-input.ts`, `from-delete-field-input-...util.ts`) rather than in the engine-level `FlatFieldMetadataValidatorService`. Moving them into the validator requires threading operation-origin (direct field mutation vs engine cascade) through the migration matrix, otherwise legitimate object rename/delete cascades (which carry `isSystemBuild=false`) would be rejected. Tracked in twentyhq/core-team-issues#2671. Review in cubic --- .../public/people-data-labs/package.json | 4 +- .../src/views/enriched-companies.view.ts | 12 +- .../src/views/enriched-people.view.ts | 12 +- .../src/metadata/generated/schema.graphql | 362 ++-- .../src/metadata/generated/schema.ts | 778 ++++---- .../src/metadata/generated/types.ts | 1072 +++++------ .../extend/apps/data/system-fields.mdx | 46 + .../apps/operations/sync-and-recovery.mdx | 2 +- .../metadata/objects/mock-objects-metadata.ts | 46 +- .../src/testing/mock-data/workflow-run.ts | 40 +- .../src/testing/mock-data/workflow.ts | 8 +- .../app-dev/expected-manifest.ts | 153 +- .../app-dev/expected-manifest.ts | 580 ------ .../app-dev/tests/manifest.tests.ts | 2 +- ...stub-twenty-sdk-define.plugin.spec.ts.snap | 1 + .../build/manifest/manifest-build.ts | 13 +- .../get-default-fields-in-object-fields.ts | 18 +- .../get-default-relation-object-fields.ts | 174 -- packages/twenty-sdk/src/sdk/define/index.ts | 5 +- ...backfill-message-thread-subject.command.ts | 3 +- .../2-23-upgrade-version-command.module.ts | 12 + ...tion-field-universal-identifier.command.ts | 276 +++ ...de-people-data-labs-application.command.ts | 112 ++ ...universal-flat-field-metadata.util.spec.ts | 17 + ...t-to-universal-flat-field-metadata.util.ts | 9 +- .../application-package.module.ts | 4 +- .../upgrade/upgrade-status.module.ts | 35 + .../core-modules/upgrade/upgrade.module.ts | 26 +- ...d-metadata-editable-properties.constant.ts | 4 + ...-relation-flat-field-metadata-pair.spec.ts | 21 +- ...from-morph-relation-update-payload.util.ts | 2 +- ...-to-flat-field-metadatas-to-create.util.ts | 2 +- ...-to-flat-field-metadatas-to-delete.util.ts | 7 + ...ield-input-to-flat-field-metadatas.util.ts | 2 +- ...ield-input-to-flat-field-metadatas.util.ts | 2 +- ...-relation-flat-field-metadata-pair.util.ts | 15 +- ...d-metadata-from-create-field-input.util.ts | 11 +- .../utils/sanitize-raw-update-field-input.ts | 28 +- ...and-flat-field-metadatas-to-create.util.ts | 26 +- ...-to-flat-field-metadatas-to-delete.util.ts | 8 + ...object-metadata-update-side-effect.util.ts | 1 + ...morph-field-on-object-names-update.util.ts | 18 +- .../metadata-side-effect-handlers.module.ts | 4 + ...create-side-effect-handler.service.spec.ts | 206 +++ ...update-side-effect-handler.service.spec.ts | 93 + ...delete-side-effect-handler.service.spec.ts | 68 + ...s-on-create-side-effect-handler.service.ts | 117 ++ ...s-on-update-side-effect-handler.service.ts | 105 ++ ...s-on-delete-side-effect-handler.service.ts | 2 +- .../object-metadata.service.ts | 30 +- ...-field-metadatas-for-custom-object.util.ts | 186 -- ...on-flat-field-metadatas-for-object.util.ts | 124 ++ ...hment-standard-flat-field-metadata.util.ts | 35 +- ...mpany-standard-flat-field-metadata.util.ts | 4 + ...board-standard-flat-field-metadata.util.ts | 2 + ...paign-standard-flat-field-metadata.util.ts | 1 + ...-list-standard-flat-field-metadata.util.ts | 1 + ...-note-standard-flat-field-metadata.util.ts | 2 + ...arget-standard-flat-field-metadata.util.ts | 15 +- ...unity-standard-flat-field-metadata.util.ts | 4 + ...erson-standard-flat-field-metadata.util.ts | 4 + ...-task-standard-flat-field-metadata.util.ts | 2 + ...arget-standard-flat-field-metadata.util.ts | 15 +- ...ivity-standard-flat-field-metadata.util.ts | 55 +- ...w-run-standard-flat-field-metadata.util.ts | 1 + ...kflow-standard-flat-field-metadata.util.ts | 2 + ...rsion-standard-flat-field-metadata.util.ts | 1 + ...ndard-relation-field-flat-metadata.util.ts | 18 +- ...on-name-collision.integration-spec.ts.snap | 111 ++ ...identifier-hijack.integration-spec.ts.snap | 119 ++ ...rkspace-migration.integration-spec.ts.snap | 1600 ++++++++++++----- ...elation-name-collision.integration-spec.ts | 123 ++ ...ctor-identifier-hijack.integration-spec.ts | 167 ++ ...on-workspace-migration.integration-spec.ts | 152 +- .../build-default-object-manifest.util.ts | 74 +- ...-universal-identifiers.integration-spec.ts | 32 +- .../rename-custom-object.integration-spec.ts | 100 +- ...on-field-universal-identifier.util.spec.ts | 52 + .../get-field-universal-identifier.util.ts | 1 - ...elation-field-universal-identifier.util.ts | 16 + .../twenty-shared/src/application/index.ts | 1 + ...ardObjectUniversalIdentifiers.test.ts.snap | 96 +- ...d-object-universal-identifiers.constant.ts | 25 + .../constants/standard-object.constant.ts | 491 ++++- packages/twenty-shared/src/metadata/index.ts | 2 + 85 files changed, 5142 insertions(+), 3086 deletions(-) delete mode 100644 packages/twenty-sdk/src/cli/utilities/build/manifest/utils/get-default-relation-object-fields.ts create mode 100644 packages/twenty-server/src/database/commands/upgrade-version-command/2-23/2-23-workspace-command-1784565136000-reconcile-system-relation-field-universal-identifier.command.ts create mode 100644 packages/twenty-server/src/database/commands/upgrade-version-command/2-23/2-23-workspace-command-1784565137000-upgrade-people-data-labs-application.command.ts create mode 100644 packages/twenty-server/src/engine/core-modules/upgrade/upgrade-status.module.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/object-metadata/services/__tests__/object-system-relations-on-create-side-effect-handler.service.spec.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/object-metadata/services/__tests__/object-system-relations-on-update-side-effect-handler.service.spec.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/object-metadata/services/object-system-relations-on-create-side-effect-handler.service.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/object-metadata/services/object-system-relations-on-update-side-effect-handler.service.ts delete mode 100644 packages/twenty-server/src/engine/metadata-modules/object-metadata/utils/build-default-relation-flat-field-metadatas-for-custom-object.util.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/object-metadata/utils/build-system-relation-flat-field-metadatas-for-object.util.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/application/__snapshots__/failing-sync-application-default-relation-name-collision.integration-spec.ts.snap create mode 100644 packages/twenty-server/test/integration/metadata/suites/application/__snapshots__/failing-sync-application-search-vector-identifier-hijack.integration-spec.ts.snap create mode 100644 packages/twenty-server/test/integration/metadata/suites/application/failing-sync-application-default-relation-name-collision.integration-spec.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/application/failing-sync-application-search-vector-identifier-hijack.integration-spec.ts create mode 100644 packages/twenty-shared/src/application/deterministic-identifier/__tests__/get-system-relation-field-universal-identifier.util.spec.ts create mode 100644 packages/twenty-shared/src/application/deterministic-identifier/get-system-relation-field-universal-identifier.util.ts create mode 100644 packages/twenty-shared/src/metadata/constants/standard-object-universal-identifiers.constant.ts diff --git a/packages/twenty-apps/public/people-data-labs/package.json b/packages/twenty-apps/public/people-data-labs/package.json index a835edf87d..fef674672a 100644 --- a/packages/twenty-apps/public/people-data-labs/package.json +++ b/packages/twenty-apps/public/people-data-labs/package.json @@ -1,13 +1,13 @@ { "name": "@twentyhq/people-data-labs", - "version": "1.0.6", + "version": "1.0.7", "description": "People Data Labs enrichment app for Twenty", "license": "MIT", "engines": { "node": "^24.5.0", "npm": "please-use-yarn", "yarn": ">=4.0.2", - "twenty": ">=2.20.0" + "twenty": ">=2.23.0" }, "keywords": [ "twenty-app" diff --git a/packages/twenty-apps/public/people-data-labs/src/views/enriched-companies.view.ts b/packages/twenty-apps/public/people-data-labs/src/views/enriched-companies.view.ts index 38cc086255..27553d027e 100644 --- a/packages/twenty-apps/public/people-data-labs/src/views/enriched-companies.view.ts +++ b/packages/twenty-apps/public/people-data-labs/src/views/enriched-companies.view.ts @@ -52,10 +52,14 @@ export default defineView({ { universalIdentifier: '0c027ae2-7e63-4354-8962-6922923929c0', fieldMetadataUniversalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.company.pdlAlternativeDomains, position: 32, isVisible: true }, { universalIdentifier: '0814427e-6699-44fd-9d57-7af6f2389f7d', fieldMetadataUniversalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.company.pdlTags, position: 33, isVisible: true }, { universalIdentifier: '65ca68c9-589f-44dc-bf57-d0a2152cb09c', fieldMetadataUniversalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.company.pdlAffiliatedProfiles, position: 34, isVisible: true }, - { universalIdentifier: '3f0e5a60-3f49-4872-8451-5344a3715374', fieldMetadataUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.fields.noteTargets.universalIdentifier, position: 35, isVisible: true }, - { universalIdentifier: '70ab74e1-d2ff-4f15-81ef-196e223a783e', fieldMetadataUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.fields.taskTargets.universalIdentifier, position: 36, isVisible: true }, - { universalIdentifier: '035b3904-59b9-4fce-a366-ee0139770346', fieldMetadataUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.fields.attachments.universalIdentifier, position: 37, isVisible: true }, - { universalIdentifier: '617b7343-1ae5-4813-b4f5-6e99d0a833bb', fieldMetadataUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.fields.timelineActivities.universalIdentifier, position: 38, isVisible: true }, + // TODO: derive these with getSystemRelationFieldUniversalIdentifier once + // the next twenty-sdk is released. The system relation field universal + // identifiers were re-derived name-free in twenty v2.23 and the pinned + // twenty-sdk still embeds the old values. + { universalIdentifier: '3f0e5a60-3f49-4872-8451-5344a3715374', fieldMetadataUniversalIdentifier: '0b28db77-8099-5ce3-a11f-86a5778d7740', position: 35, isVisible: true }, // company.noteTargets + { universalIdentifier: '70ab74e1-d2ff-4f15-81ef-196e223a783e', fieldMetadataUniversalIdentifier: '9d7b0087-7037-55dc-bb59-456064455694', position: 36, isVisible: true }, // company.taskTargets + { universalIdentifier: '035b3904-59b9-4fce-a366-ee0139770346', fieldMetadataUniversalIdentifier: 'a4399ae3-fe89-59fe-a282-aec77d4e63a8', position: 37, isVisible: true }, // company.attachments + { universalIdentifier: '617b7343-1ae5-4813-b4f5-6e99d0a833bb', fieldMetadataUniversalIdentifier: '28ec0f8f-ad23-5317-b2d9-1a487bcefee2', position: 38, isVisible: true }, // company.timelineActivities { universalIdentifier: '24d0411e-23d2-4cf8-9781-6efc8443efdc', fieldMetadataUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.fields.createdBy.universalIdentifier, position: 39, isVisible: true }, { universalIdentifier: '4036071d-e527-46c8-940c-401f33803aa9', fieldMetadataUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.fields.updatedBy.universalIdentifier, position: 40, isVisible: true }, { universalIdentifier: 'ab123fcd-ec53-4d33-b43c-c7319db72be4', fieldMetadataUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.fields.createdAt.universalIdentifier, position: 41, isVisible: true }, diff --git a/packages/twenty-apps/public/people-data-labs/src/views/enriched-people.view.ts b/packages/twenty-apps/public/people-data-labs/src/views/enriched-people.view.ts index 53a2052c61..4f00bc693b 100644 --- a/packages/twenty-apps/public/people-data-labs/src/views/enriched-people.view.ts +++ b/packages/twenty-apps/public/people-data-labs/src/views/enriched-people.view.ts @@ -56,10 +56,14 @@ export default defineView({ { universalIdentifier: '730dfb73-0ff7-429a-a70b-fefc6080b537', fieldMetadataUniversalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlSex, position: 36, isVisible: true }, { universalIdentifier: '1620bf1d-0b12-4f69-ad0e-ba7dd1d8aa44', fieldMetadataUniversalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlBirthDate, position: 37, isVisible: true }, { universalIdentifier: '029770cb-9353-4b5f-8395-b217cc95402e', fieldMetadataUniversalIdentifier: PDL_FIELD_UNIVERSAL_IDENTIFIERS.person.pdlBirthYear, position: 38, isVisible: true }, - { universalIdentifier: 'ef6e496c-de6c-47ed-bd44-886736f942ec', fieldMetadataUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.fields.noteTargets.universalIdentifier, position: 39, isVisible: true }, - { universalIdentifier: '3758ed96-f76d-49a0-b37b-7c5c9aa4c1c1', fieldMetadataUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.fields.taskTargets.universalIdentifier, position: 40, isVisible: true }, - { universalIdentifier: 'a5bf7046-8fc9-4704-91a5-ba6c1b0f4b58', fieldMetadataUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.fields.attachments.universalIdentifier, position: 41, isVisible: true }, - { universalIdentifier: 'f6ed39c3-aff2-4ccf-b4e8-57de72b64cdd', fieldMetadataUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.fields.timelineActivities.universalIdentifier, position: 42, isVisible: true }, + // TODO: derive these with getSystemRelationFieldUniversalIdentifier once + // the next twenty-sdk is released. The system relation field universal + // identifiers were re-derived name-free in twenty v2.23 and the pinned + // twenty-sdk still embeds the old values. + { universalIdentifier: 'ef6e496c-de6c-47ed-bd44-886736f942ec', fieldMetadataUniversalIdentifier: '1c0e0dfe-a01f-58d6-8948-08b93164d03c', position: 39, isVisible: true }, // person.noteTargets + { universalIdentifier: '3758ed96-f76d-49a0-b37b-7c5c9aa4c1c1', fieldMetadataUniversalIdentifier: 'ddea86e6-5766-5798-ac32-14fbab6089f6', position: 40, isVisible: true }, // person.taskTargets + { universalIdentifier: 'a5bf7046-8fc9-4704-91a5-ba6c1b0f4b58', fieldMetadataUniversalIdentifier: '54dd92e1-67bf-5381-86f3-c747976dcdaf', position: 41, isVisible: true }, // person.attachments + { universalIdentifier: 'f6ed39c3-aff2-4ccf-b4e8-57de72b64cdd', fieldMetadataUniversalIdentifier: '32d7483f-6227-528b-bc65-62d9cf858cd8', position: 42, isVisible: true }, // person.timelineActivities { universalIdentifier: '45159f0c-bac0-439c-ad8d-c7e4fce4b457', fieldMetadataUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.fields.createdBy.universalIdentifier, position: 43, isVisible: true }, { universalIdentifier: '8a5f1742-317f-4b01-b0c1-5d0866f78fa9', fieldMetadataUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.fields.updatedBy.universalIdentifier, position: 44, isVisible: true }, { universalIdentifier: '1614c9fb-3566-481f-9dfb-bbba37b1c8aa', fieldMetadataUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.fields.createdAt.universalIdentifier, position: 45, isVisible: true }, diff --git a/packages/twenty-client-sdk/src/metadata/generated/schema.graphql b/packages/twenty-client-sdk/src/metadata/generated/schema.graphql index 3f6d537c74..f0b9964a63 100644 --- a/packages/twenty-client-sdk/src/metadata/generated/schema.graphql +++ b/packages/twenty-client-sdk/src/metadata/generated/schema.graphql @@ -1976,113 +1976,6 @@ type RotateClientSecret { clientSecret: String! } -type Relation { - type: RelationType! - sourceObjectMetadata: Object! - targetObjectMetadata: Object! - sourceFieldMetadata: Field! - targetFieldMetadata: Field! -} - -"""Relation type""" -enum RelationType { - ONE_TO_MANY - MANY_TO_ONE -} - -type IndexField { - id: UUID! - fieldMetadataId: UUID! - order: Float! - subFieldName: String - createdAt: DateTime! - updatedAt: DateTime! -} - -type ObjectRecordCount { - objectNamePlural: String! - totalCount: Int! -} - -type SearchField { - id: UUID! - fieldMetadataId: UUID! - tsVectorFieldMetadataId: UUID! - position: Float! - createdAt: DateTime! - updatedAt: DateTime! -} - -type ObjectEdge { - """The node containing the Object""" - node: Object! - - """Cursor for this node.""" - cursor: ConnectionCursor! -} - -type PageInfo { - """true if paging forward and there are more records.""" - hasNextPage: Boolean - - """true if paging backwards and there are more records.""" - hasPreviousPage: Boolean - - """The cursor of the first returned record.""" - startCursor: ConnectionCursor - - """The cursor of the last returned record.""" - endCursor: ConnectionCursor -} - -type ObjectConnection { - """Paging information""" - pageInfo: PageInfo! - - """Array of edges.""" - edges: [ObjectEdge!]! -} - -type IndexEdge { - """The node containing the Index""" - node: Index! - - """Cursor for this node.""" - cursor: ConnectionCursor! -} - -type ObjectIndexMetadatasConnection { - """Paging information""" - pageInfo: PageInfo! - - """Array of edges.""" - edges: [IndexEdge!]! -} - -type FieldEdge { - """The node containing the Field""" - node: Field! - - """Cursor for this node.""" - cursor: ConnectionCursor! -} - -type ObjectFieldsConnection { - """Paging information""" - pageInfo: PageInfo! - - """Array of edges.""" - edges: [FieldEdge!]! -} - -type FieldConnection { - """Paging information""" - pageInfo: PageInfo! - - """Array of edges.""" - edges: [FieldEdge!]! -} - type AppConnection { id: ID! providerName: String! @@ -2234,6 +2127,113 @@ type MarketplaceAppDetail { manifest: JSON @deprecated(reason: "Use the explicit MarketplaceAppDetail fields (description, author, roles, ...) instead") } +type Relation { + type: RelationType! + sourceObjectMetadata: Object! + targetObjectMetadata: Object! + sourceFieldMetadata: Field! + targetFieldMetadata: Field! +} + +"""Relation type""" +enum RelationType { + ONE_TO_MANY + MANY_TO_ONE +} + +type IndexField { + id: UUID! + fieldMetadataId: UUID! + order: Float! + subFieldName: String + createdAt: DateTime! + updatedAt: DateTime! +} + +type ObjectRecordCount { + objectNamePlural: String! + totalCount: Int! +} + +type SearchField { + id: UUID! + fieldMetadataId: UUID! + tsVectorFieldMetadataId: UUID! + position: Float! + createdAt: DateTime! + updatedAt: DateTime! +} + +type ObjectEdge { + """The node containing the Object""" + node: Object! + + """Cursor for this node.""" + cursor: ConnectionCursor! +} + +type PageInfo { + """true if paging forward and there are more records.""" + hasNextPage: Boolean + + """true if paging backwards and there are more records.""" + hasPreviousPage: Boolean + + """The cursor of the first returned record.""" + startCursor: ConnectionCursor + + """The cursor of the last returned record.""" + endCursor: ConnectionCursor +} + +type ObjectConnection { + """Paging information""" + pageInfo: PageInfo! + + """Array of edges.""" + edges: [ObjectEdge!]! +} + +type IndexEdge { + """The node containing the Index""" + node: Index! + + """Cursor for this node.""" + cursor: ConnectionCursor! +} + +type ObjectIndexMetadatasConnection { + """Paging information""" + pageInfo: PageInfo! + + """Array of edges.""" + edges: [IndexEdge!]! +} + +type FieldEdge { + """The node containing the Field""" + node: Field! + + """Cursor for this node.""" + cursor: ConnectionCursor! +} + +type ObjectFieldsConnection { + """Paging information""" + pageInfo: PageInfo! + + """Array of edges.""" + edges: [FieldEdge!]! +} + +type FieldConnection { + """Paging information""" + pageInfo: PageInfo! + + """Array of edges.""" + edges: [FieldEdge!]! +} + type BillingEntitlement { key: BillingEntitlementKey! value: Boolean! @@ -3102,6 +3102,21 @@ type Query { currentWorkspace: Workspace! getPublicWorkspaceDataByDomain(origin: String): PublicWorkspaceData! getPublicWorkspaceDataById(id: UUID!): PublicWorkspaceDataSummary! + findApplicationRegistrationByClientId(clientId: String!): PublicApplicationRegistration + findApplicationRegistrationByUniversalIdentifier(universalIdentifier: String!): ApplicationRegistration + findManyApplicationRegistrations: [ApplicationRegistration!]! + findOneApplicationRegistration(id: String!): ApplicationRegistration! + findApplicationRegistrationStats(id: String!): ApplicationRegistrationStats! + findApplicationRegistrationVariables(applicationRegistrationId: String!): [ApplicationRegistrationVariableDTO!]! + applicationRegistrationTarballUrl(id: String!): String + findClaimableApplicationRegistration(sourcePackage: String, universalIdentifier: String): ClaimableApplicationRegistration + githubClaimAuthorizationUrl(applicationRegistrationId: String!): String! + findManyApplications: [Application!]! + findOneApplication(id: UUID, universalIdentifier: UUID): Application! + findManyMarketplaceApps(universalIdentifiers: [String!]): [MarketplaceApp!]! + findMarketplaceAppDetail(universalIdentifier: String!): MarketplaceAppDetail! + publicMarketplaceApps(isVetted: Boolean! = true): [MarketplaceApp!]! + publicMarketplaceAppDetail(universalIdentifier: String!): MarketplaceAppDetail! field( """The id of the record to find.""" id: UUID! @@ -3115,21 +3130,6 @@ type Query { ): FieldConnection! getViewGroups(viewId: String): [ViewGroup!]! getViewGroup(id: String!): ViewGroup - findManyApplications: [Application!]! - findOneApplication(id: UUID, universalIdentifier: UUID): Application! - findManyMarketplaceApps(universalIdentifiers: [String!]): [MarketplaceApp!]! - findMarketplaceAppDetail(universalIdentifier: String!): MarketplaceAppDetail! - publicMarketplaceApps(isVetted: Boolean! = true): [MarketplaceApp!]! - publicMarketplaceAppDetail(universalIdentifier: String!): MarketplaceAppDetail! - findApplicationRegistrationByClientId(clientId: String!): PublicApplicationRegistration - findApplicationRegistrationByUniversalIdentifier(universalIdentifier: String!): ApplicationRegistration - findManyApplicationRegistrations: [ApplicationRegistration!]! - findOneApplicationRegistration(id: String!): ApplicationRegistration! - findApplicationRegistrationStats(id: String!): ApplicationRegistrationStats! - findApplicationRegistrationVariables(applicationRegistrationId: String!): [ApplicationRegistrationVariableDTO!]! - applicationRegistrationTarballUrl(id: String!): String - findClaimableApplicationRegistration(sourcePackage: String, universalIdentifier: String): ClaimableApplicationRegistration - githubClaimAuthorizationUrl(applicationRegistrationId: String!): String! getRoles: [Role!]! previewMessageCampaignAudience(input: PreviewMessageCampaignAudienceInput!): CampaignAudiencePreviewDTO! unsubscribeTopics: [UnsubscribeTopic!]! @@ -3380,20 +3380,7 @@ type Mutation { updateWorkspace(data: UpdateWorkspaceInput!): Workspace! deleteCurrentWorkspace: Workspace! checkCustomDomainValidRecords: DomainValidRecords - createOneField(input: CreateOneFieldMetadataInput!): Field! - updateOneField(input: UpdateOneFieldMetadataInput!): Field! - deleteOneField(input: DeleteOneFieldInput!): Field! - createViewGroup(input: CreateViewGroupInput!): ViewGroup! - createManyViewGroups(inputs: [CreateViewGroupInput!]!): [ViewGroup!]! - updateViewGroup(input: UpdateViewGroupInput!): ViewGroup! - updateManyViewGroups(inputs: [UpdateViewGroupInput!]!): [ViewGroup!]! - deleteViewGroup(input: DeleteViewGroupInput!): ViewGroup! - destroyViewGroup(input: DestroyViewGroupInput!): ViewGroup! - installMarketplaceApp(universalIdentifier: String!, version: String): Boolean! @deprecated(reason: "Use installApplication instead") - installApplication(universalIdentifier: String!, version: String): Application! - updateApplication(id: UUID!, input: UpdateApplicationInput!): Application! - uninstallApplication(universalIdentifier: String!): Boolean! - syncMarketplaceCatalog: Boolean! + upgradeApplication(appRegistrationId: String!, targetVersion: String!): Boolean! createApplicationRegistration(input: CreateApplicationRegistrationInput!): CreateApplicationRegistration! updateApplicationRegistration(input: UpdateApplicationRegistrationInput!): ApplicationRegistration! deleteApplicationRegistration(id: String!): Boolean! @@ -3404,6 +3391,20 @@ type Mutation { uploadAppTarball(file: Upload!, universalIdentifier: String): ApplicationRegistration! claimApplicationRegistrationOwnership(applicationRegistrationId: String!): ApplicationRegistration! transferApplicationRegistrationOwnership(applicationRegistrationId: String!, targetWorkspaceSubdomain: String!): ApplicationRegistration! + installMarketplaceApp(universalIdentifier: String!, version: String): Boolean! @deprecated(reason: "Use installApplication instead") + installApplication(universalIdentifier: String!, version: String): Application! + updateApplication(id: UUID!, input: UpdateApplicationInput!): Application! + uninstallApplication(universalIdentifier: String!): Boolean! + syncMarketplaceCatalog: Boolean! + createOneField(input: CreateOneFieldMetadataInput!): Field! + updateOneField(input: UpdateOneFieldMetadataInput!): Field! + deleteOneField(input: DeleteOneFieldInput!): Field! + createViewGroup(input: CreateViewGroupInput!): ViewGroup! + createManyViewGroups(inputs: [CreateViewGroupInput!]!): [ViewGroup!]! + updateViewGroup(input: UpdateViewGroupInput!): ViewGroup! + updateManyViewGroups(inputs: [UpdateViewGroupInput!]!): [ViewGroup!]! + deleteViewGroup(input: DeleteViewGroupInput!): ViewGroup! + destroyViewGroup(input: DestroyViewGroupInput!): ViewGroup! updateWorkspaceMemberRole(workspaceMemberId: UUID!, roleId: UUID!): WorkspaceMember! createOneRole(createRoleInput: CreateRoleInput!): Role! updateOneRole(updateRoleInput: UpdateRoleInput!): Role! @@ -3496,7 +3497,6 @@ type Mutation { createDevelopmentApplication(universalIdentifier: String!, name: String!): DevelopmentApplication! syncApplication(manifest: JSON!, dryRun: Boolean): WorkspaceMigration! uploadApplicationFile(file: Upload!, applicationUniversalIdentifier: String!, fileFolder: FileFolder!, filePath: String!): File! - upgradeApplication(appRegistrationId: String!, targetVersion: String!): Boolean! generateApplicationToken(applicationId: UUID!): ApplicationTokenPair! renewApplicationToken(applicationRefreshToken: String!): ApplicationTokenPair! } @@ -4200,6 +4200,50 @@ input UpdateWorkspaceInput { isInternalMessagesImportEnabled: Boolean } +input CreateApplicationRegistrationInput { + name: String! + universalIdentifier: String + oAuthRedirectUris: [String!] + oAuthScopes: [String!] +} + +input UpdateApplicationRegistrationInput { + id: String! + update: UpdateApplicationRegistrationPayload! +} + +input UpdateApplicationRegistrationPayload { + name: String + oAuthRedirectUris: [String!] + oAuthScopes: [String!] + isListed: Boolean + isPreInstalled: Boolean + isVetted: Boolean +} + +input CreateApplicationRegistrationVariableInput { + applicationRegistrationId: String! + key: String! + value: String! + description: String + isSecret: Boolean +} + +input UpdateApplicationRegistrationVariableInput { + id: String! + update: UpdateApplicationRegistrationVariablePayload! +} + +input UpdateApplicationRegistrationVariablePayload { + value: String + resetValue: Boolean + description: String +} + +input UpdateApplicationInput { + autoUpgrade: Boolean +} + input CreateOneFieldMetadataInput { """The record to create""" field: CreateFieldInput! @@ -4293,50 +4337,6 @@ input DestroyViewGroupInput { id: UUID! } -input UpdateApplicationInput { - autoUpgrade: Boolean -} - -input CreateApplicationRegistrationInput { - name: String! - universalIdentifier: String - oAuthRedirectUris: [String!] - oAuthScopes: [String!] -} - -input UpdateApplicationRegistrationInput { - id: String! - update: UpdateApplicationRegistrationPayload! -} - -input UpdateApplicationRegistrationPayload { - name: String - oAuthRedirectUris: [String!] - oAuthScopes: [String!] - isListed: Boolean - isPreInstalled: Boolean - isVetted: Boolean -} - -input CreateApplicationRegistrationVariableInput { - applicationRegistrationId: String! - key: String! - value: String! - description: String - isSecret: Boolean -} - -input UpdateApplicationRegistrationVariableInput { - id: String! - update: UpdateApplicationRegistrationVariablePayload! -} - -input UpdateApplicationRegistrationVariablePayload { - value: String - resetValue: Boolean - description: String -} - input CreateRoleInput { id: String label: String! diff --git a/packages/twenty-client-sdk/src/metadata/generated/schema.ts b/packages/twenty-client-sdk/src/metadata/generated/schema.ts index 59501011f5..b6cbda603a 100644 --- a/packages/twenty-client-sdk/src/metadata/generated/schema.ts +++ b/packages/twenty-client-sdk/src/metadata/generated/schema.ts @@ -1636,113 +1636,6 @@ export interface RotateClientSecret { __typename: 'RotateClientSecret' } -export interface Relation { - type: RelationType - sourceObjectMetadata: Object - targetObjectMetadata: Object - sourceFieldMetadata: Field - targetFieldMetadata: Field - __typename: 'Relation' -} - - -/** Relation type */ -export type RelationType = 'ONE_TO_MANY' | 'MANY_TO_ONE' - -export interface IndexField { - id: Scalars['UUID'] - fieldMetadataId: Scalars['UUID'] - order: Scalars['Float'] - subFieldName?: Scalars['String'] - createdAt: Scalars['DateTime'] - updatedAt: Scalars['DateTime'] - __typename: 'IndexField' -} - -export interface ObjectRecordCount { - objectNamePlural: Scalars['String'] - totalCount: Scalars['Int'] - __typename: 'ObjectRecordCount' -} - -export interface SearchField { - id: Scalars['UUID'] - fieldMetadataId: Scalars['UUID'] - tsVectorFieldMetadataId: Scalars['UUID'] - position: Scalars['Float'] - createdAt: Scalars['DateTime'] - updatedAt: Scalars['DateTime'] - __typename: 'SearchField' -} - -export interface ObjectEdge { - /** The node containing the Object */ - node: Object - /** Cursor for this node. */ - cursor: Scalars['ConnectionCursor'] - __typename: 'ObjectEdge' -} - -export interface PageInfo { - /** true if paging forward and there are more records. */ - hasNextPage?: Scalars['Boolean'] - /** true if paging backwards and there are more records. */ - hasPreviousPage?: Scalars['Boolean'] - /** The cursor of the first returned record. */ - startCursor?: Scalars['ConnectionCursor'] - /** The cursor of the last returned record. */ - endCursor?: Scalars['ConnectionCursor'] - __typename: 'PageInfo' -} - -export interface ObjectConnection { - /** Paging information */ - pageInfo: PageInfo - /** Array of edges. */ - edges: ObjectEdge[] - __typename: 'ObjectConnection' -} - -export interface IndexEdge { - /** The node containing the Index */ - node: Index - /** Cursor for this node. */ - cursor: Scalars['ConnectionCursor'] - __typename: 'IndexEdge' -} - -export interface ObjectIndexMetadatasConnection { - /** Paging information */ - pageInfo: PageInfo - /** Array of edges. */ - edges: IndexEdge[] - __typename: 'ObjectIndexMetadatasConnection' -} - -export interface FieldEdge { - /** The node containing the Field */ - node: Field - /** Cursor for this node. */ - cursor: Scalars['ConnectionCursor'] - __typename: 'FieldEdge' -} - -export interface ObjectFieldsConnection { - /** Paging information */ - pageInfo: PageInfo - /** Array of edges. */ - edges: FieldEdge[] - __typename: 'ObjectFieldsConnection' -} - -export interface FieldConnection { - /** Paging information */ - pageInfo: PageInfo - /** Array of edges. */ - edges: FieldEdge[] - __typename: 'FieldConnection' -} - export interface AppConnection { id: Scalars['ID'] providerName: Scalars['String'] @@ -1912,6 +1805,113 @@ export interface MarketplaceAppDetail { __typename: 'MarketplaceAppDetail' } +export interface Relation { + type: RelationType + sourceObjectMetadata: Object + targetObjectMetadata: Object + sourceFieldMetadata: Field + targetFieldMetadata: Field + __typename: 'Relation' +} + + +/** Relation type */ +export type RelationType = 'ONE_TO_MANY' | 'MANY_TO_ONE' + +export interface IndexField { + id: Scalars['UUID'] + fieldMetadataId: Scalars['UUID'] + order: Scalars['Float'] + subFieldName?: Scalars['String'] + createdAt: Scalars['DateTime'] + updatedAt: Scalars['DateTime'] + __typename: 'IndexField' +} + +export interface ObjectRecordCount { + objectNamePlural: Scalars['String'] + totalCount: Scalars['Int'] + __typename: 'ObjectRecordCount' +} + +export interface SearchField { + id: Scalars['UUID'] + fieldMetadataId: Scalars['UUID'] + tsVectorFieldMetadataId: Scalars['UUID'] + position: Scalars['Float'] + createdAt: Scalars['DateTime'] + updatedAt: Scalars['DateTime'] + __typename: 'SearchField' +} + +export interface ObjectEdge { + /** The node containing the Object */ + node: Object + /** Cursor for this node. */ + cursor: Scalars['ConnectionCursor'] + __typename: 'ObjectEdge' +} + +export interface PageInfo { + /** true if paging forward and there are more records. */ + hasNextPage?: Scalars['Boolean'] + /** true if paging backwards and there are more records. */ + hasPreviousPage?: Scalars['Boolean'] + /** The cursor of the first returned record. */ + startCursor?: Scalars['ConnectionCursor'] + /** The cursor of the last returned record. */ + endCursor?: Scalars['ConnectionCursor'] + __typename: 'PageInfo' +} + +export interface ObjectConnection { + /** Paging information */ + pageInfo: PageInfo + /** Array of edges. */ + edges: ObjectEdge[] + __typename: 'ObjectConnection' +} + +export interface IndexEdge { + /** The node containing the Index */ + node: Index + /** Cursor for this node. */ + cursor: Scalars['ConnectionCursor'] + __typename: 'IndexEdge' +} + +export interface ObjectIndexMetadatasConnection { + /** Paging information */ + pageInfo: PageInfo + /** Array of edges. */ + edges: IndexEdge[] + __typename: 'ObjectIndexMetadatasConnection' +} + +export interface FieldEdge { + /** The node containing the Field */ + node: Field + /** Cursor for this node. */ + cursor: Scalars['ConnectionCursor'] + __typename: 'FieldEdge' +} + +export interface ObjectFieldsConnection { + /** Paging information */ + pageInfo: PageInfo + /** Array of edges. */ + edges: FieldEdge[] + __typename: 'ObjectFieldsConnection' +} + +export interface FieldConnection { + /** Paging information */ + pageInfo: PageInfo + /** Array of edges. */ + edges: FieldEdge[] + __typename: 'FieldConnection' +} + export interface BillingEntitlement { key: BillingEntitlementKey value: Scalars['Boolean'] @@ -2751,16 +2751,6 @@ export interface Query { currentWorkspace: Workspace getPublicWorkspaceDataByDomain: PublicWorkspaceData getPublicWorkspaceDataById: PublicWorkspaceDataSummary - field: Field - fields: FieldConnection - getViewGroups: ViewGroup[] - getViewGroup?: ViewGroup - findManyApplications: Application[] - findOneApplication: Application - findManyMarketplaceApps: MarketplaceApp[] - findMarketplaceAppDetail: MarketplaceAppDetail - publicMarketplaceApps: MarketplaceApp[] - publicMarketplaceAppDetail: MarketplaceAppDetail findApplicationRegistrationByClientId?: PublicApplicationRegistration findApplicationRegistrationByUniversalIdentifier?: ApplicationRegistration findManyApplicationRegistrations: ApplicationRegistration[] @@ -2770,6 +2760,16 @@ export interface Query { applicationRegistrationTarballUrl?: Scalars['String'] findClaimableApplicationRegistration?: ClaimableApplicationRegistration githubClaimAuthorizationUrl: Scalars['String'] + findManyApplications: Application[] + findOneApplication: Application + findManyMarketplaceApps: MarketplaceApp[] + findMarketplaceAppDetail: MarketplaceAppDetail + publicMarketplaceApps: MarketplaceApp[] + publicMarketplaceAppDetail: MarketplaceAppDetail + field: Field + fields: FieldConnection + getViewGroups: ViewGroup[] + getViewGroup?: ViewGroup getRoles: Role[] previewMessageCampaignAudience: CampaignAudiencePreviewDTO unsubscribeTopics: UnsubscribeTopic[] @@ -2927,21 +2927,7 @@ export interface Mutation { updateWorkspace: Workspace deleteCurrentWorkspace: Workspace checkCustomDomainValidRecords?: DomainValidRecords - createOneField: Field - updateOneField: Field - deleteOneField: Field - createViewGroup: ViewGroup - createManyViewGroups: ViewGroup[] - updateViewGroup: ViewGroup - updateManyViewGroups: ViewGroup[] - deleteViewGroup: ViewGroup - destroyViewGroup: ViewGroup - /** @deprecated Use installApplication instead */ - installMarketplaceApp: Scalars['Boolean'] - installApplication: Application - updateApplication: Application - uninstallApplication: Scalars['Boolean'] - syncMarketplaceCatalog: Scalars['Boolean'] + upgradeApplication: Scalars['Boolean'] createApplicationRegistration: CreateApplicationRegistration updateApplicationRegistration: ApplicationRegistration deleteApplicationRegistration: Scalars['Boolean'] @@ -2952,6 +2938,21 @@ export interface Mutation { uploadAppTarball: ApplicationRegistration claimApplicationRegistrationOwnership: ApplicationRegistration transferApplicationRegistrationOwnership: ApplicationRegistration + /** @deprecated Use installApplication instead */ + installMarketplaceApp: Scalars['Boolean'] + installApplication: Application + updateApplication: Application + uninstallApplication: Scalars['Boolean'] + syncMarketplaceCatalog: Scalars['Boolean'] + createOneField: Field + updateOneField: Field + deleteOneField: Field + createViewGroup: ViewGroup + createManyViewGroups: ViewGroup[] + updateViewGroup: ViewGroup + updateManyViewGroups: ViewGroup[] + deleteViewGroup: ViewGroup + destroyViewGroup: ViewGroup updateWorkspaceMemberRole: WorkspaceMember createOneRole: Role updateOneRole: Role @@ -3044,7 +3045,6 @@ export interface Mutation { createDevelopmentApplication: DevelopmentApplication syncApplication: WorkspaceMigration uploadApplicationFile: File - upgradeApplication: Scalars['Boolean'] generateApplicationToken: ApplicationTokenPair renewApplicationToken: ApplicationTokenPair __typename: 'Mutation' @@ -4749,121 +4749,6 @@ export interface RotateClientSecretGenqlSelection{ __scalar?: boolean | number } -export interface RelationGenqlSelection{ - type?: boolean | number - sourceObjectMetadata?: ObjectGenqlSelection - targetObjectMetadata?: ObjectGenqlSelection - sourceFieldMetadata?: FieldGenqlSelection - targetFieldMetadata?: FieldGenqlSelection - __typename?: boolean | number - __scalar?: boolean | number -} - -export interface IndexFieldGenqlSelection{ - id?: boolean | number - fieldMetadataId?: boolean | number - order?: boolean | number - subFieldName?: boolean | number - createdAt?: boolean | number - updatedAt?: boolean | number - __typename?: boolean | number - __scalar?: boolean | number -} - -export interface ObjectRecordCountGenqlSelection{ - objectNamePlural?: boolean | number - totalCount?: boolean | number - __typename?: boolean | number - __scalar?: boolean | number -} - -export interface SearchFieldGenqlSelection{ - id?: boolean | number - fieldMetadataId?: boolean | number - tsVectorFieldMetadataId?: boolean | number - position?: boolean | number - createdAt?: boolean | number - updatedAt?: boolean | number - __typename?: boolean | number - __scalar?: boolean | number -} - -export interface ObjectEdgeGenqlSelection{ - /** The node containing the Object */ - node?: ObjectGenqlSelection - /** Cursor for this node. */ - cursor?: boolean | number - __typename?: boolean | number - __scalar?: boolean | number -} - -export interface PageInfoGenqlSelection{ - /** true if paging forward and there are more records. */ - hasNextPage?: boolean | number - /** true if paging backwards and there are more records. */ - hasPreviousPage?: boolean | number - /** The cursor of the first returned record. */ - startCursor?: boolean | number - /** The cursor of the last returned record. */ - endCursor?: boolean | number - __typename?: boolean | number - __scalar?: boolean | number -} - -export interface ObjectConnectionGenqlSelection{ - /** Paging information */ - pageInfo?: PageInfoGenqlSelection - /** Array of edges. */ - edges?: ObjectEdgeGenqlSelection - __typename?: boolean | number - __scalar?: boolean | number -} - -export interface IndexEdgeGenqlSelection{ - /** The node containing the Index */ - node?: IndexGenqlSelection - /** Cursor for this node. */ - cursor?: boolean | number - __typename?: boolean | number - __scalar?: boolean | number -} - -export interface ObjectIndexMetadatasConnectionGenqlSelection{ - /** Paging information */ - pageInfo?: PageInfoGenqlSelection - /** Array of edges. */ - edges?: IndexEdgeGenqlSelection - __typename?: boolean | number - __scalar?: boolean | number -} - -export interface FieldEdgeGenqlSelection{ - /** The node containing the Field */ - node?: FieldGenqlSelection - /** Cursor for this node. */ - cursor?: boolean | number - __typename?: boolean | number - __scalar?: boolean | number -} - -export interface ObjectFieldsConnectionGenqlSelection{ - /** Paging information */ - pageInfo?: PageInfoGenqlSelection - /** Array of edges. */ - edges?: FieldEdgeGenqlSelection - __typename?: boolean | number - __scalar?: boolean | number -} - -export interface FieldConnectionGenqlSelection{ - /** Paging information */ - pageInfo?: PageInfoGenqlSelection - /** Array of edges. */ - edges?: FieldEdgeGenqlSelection - __typename?: boolean | number - __scalar?: boolean | number -} - export interface AppConnectionGenqlSelection{ id?: boolean | number providerName?: boolean | number @@ -5049,6 +4934,121 @@ export interface MarketplaceAppDetailGenqlSelection{ __scalar?: boolean | number } +export interface RelationGenqlSelection{ + type?: boolean | number + sourceObjectMetadata?: ObjectGenqlSelection + targetObjectMetadata?: ObjectGenqlSelection + sourceFieldMetadata?: FieldGenqlSelection + targetFieldMetadata?: FieldGenqlSelection + __typename?: boolean | number + __scalar?: boolean | number +} + +export interface IndexFieldGenqlSelection{ + id?: boolean | number + fieldMetadataId?: boolean | number + order?: boolean | number + subFieldName?: boolean | number + createdAt?: boolean | number + updatedAt?: boolean | number + __typename?: boolean | number + __scalar?: boolean | number +} + +export interface ObjectRecordCountGenqlSelection{ + objectNamePlural?: boolean | number + totalCount?: boolean | number + __typename?: boolean | number + __scalar?: boolean | number +} + +export interface SearchFieldGenqlSelection{ + id?: boolean | number + fieldMetadataId?: boolean | number + tsVectorFieldMetadataId?: boolean | number + position?: boolean | number + createdAt?: boolean | number + updatedAt?: boolean | number + __typename?: boolean | number + __scalar?: boolean | number +} + +export interface ObjectEdgeGenqlSelection{ + /** The node containing the Object */ + node?: ObjectGenqlSelection + /** Cursor for this node. */ + cursor?: boolean | number + __typename?: boolean | number + __scalar?: boolean | number +} + +export interface PageInfoGenqlSelection{ + /** true if paging forward and there are more records. */ + hasNextPage?: boolean | number + /** true if paging backwards and there are more records. */ + hasPreviousPage?: boolean | number + /** The cursor of the first returned record. */ + startCursor?: boolean | number + /** The cursor of the last returned record. */ + endCursor?: boolean | number + __typename?: boolean | number + __scalar?: boolean | number +} + +export interface ObjectConnectionGenqlSelection{ + /** Paging information */ + pageInfo?: PageInfoGenqlSelection + /** Array of edges. */ + edges?: ObjectEdgeGenqlSelection + __typename?: boolean | number + __scalar?: boolean | number +} + +export interface IndexEdgeGenqlSelection{ + /** The node containing the Index */ + node?: IndexGenqlSelection + /** Cursor for this node. */ + cursor?: boolean | number + __typename?: boolean | number + __scalar?: boolean | number +} + +export interface ObjectIndexMetadatasConnectionGenqlSelection{ + /** Paging information */ + pageInfo?: PageInfoGenqlSelection + /** Array of edges. */ + edges?: IndexEdgeGenqlSelection + __typename?: boolean | number + __scalar?: boolean | number +} + +export interface FieldEdgeGenqlSelection{ + /** The node containing the Field */ + node?: FieldGenqlSelection + /** Cursor for this node. */ + cursor?: boolean | number + __typename?: boolean | number + __scalar?: boolean | number +} + +export interface ObjectFieldsConnectionGenqlSelection{ + /** Paging information */ + pageInfo?: PageInfoGenqlSelection + /** Array of edges. */ + edges?: FieldEdgeGenqlSelection + __typename?: boolean | number + __scalar?: boolean | number +} + +export interface FieldConnectionGenqlSelection{ + /** Paging information */ + pageInfo?: PageInfoGenqlSelection + /** Array of edges. */ + edges?: FieldEdgeGenqlSelection + __typename?: boolean | number + __scalar?: boolean | number +} + export interface BillingEntitlementGenqlSelection{ key?: boolean | number value?: boolean | number @@ -5950,6 +5950,21 @@ export interface QueryGenqlSelection{ currentWorkspace?: WorkspaceGenqlSelection getPublicWorkspaceDataByDomain?: (PublicWorkspaceDataGenqlSelection & { __args?: {origin?: (Scalars['String'] | null)} }) getPublicWorkspaceDataById?: (PublicWorkspaceDataSummaryGenqlSelection & { __args: {id: Scalars['UUID']} }) + findApplicationRegistrationByClientId?: (PublicApplicationRegistrationGenqlSelection & { __args: {clientId: Scalars['String']} }) + findApplicationRegistrationByUniversalIdentifier?: (ApplicationRegistrationGenqlSelection & { __args: {universalIdentifier: Scalars['String']} }) + findManyApplicationRegistrations?: ApplicationRegistrationGenqlSelection + findOneApplicationRegistration?: (ApplicationRegistrationGenqlSelection & { __args: {id: Scalars['String']} }) + findApplicationRegistrationStats?: (ApplicationRegistrationStatsGenqlSelection & { __args: {id: Scalars['String']} }) + findApplicationRegistrationVariables?: (ApplicationRegistrationVariableDTOGenqlSelection & { __args: {applicationRegistrationId: Scalars['String']} }) + applicationRegistrationTarballUrl?: { __args: {id: Scalars['String']} } + findClaimableApplicationRegistration?: (ClaimableApplicationRegistrationGenqlSelection & { __args?: {sourcePackage?: (Scalars['String'] | null), universalIdentifier?: (Scalars['String'] | null)} }) + githubClaimAuthorizationUrl?: { __args: {applicationRegistrationId: Scalars['String']} } + findManyApplications?: ApplicationGenqlSelection + findOneApplication?: (ApplicationGenqlSelection & { __args?: {id?: (Scalars['UUID'] | null), universalIdentifier?: (Scalars['UUID'] | null)} }) + findManyMarketplaceApps?: (MarketplaceAppGenqlSelection & { __args?: {universalIdentifiers?: (Scalars['String'][] | null)} }) + findMarketplaceAppDetail?: (MarketplaceAppDetailGenqlSelection & { __args: {universalIdentifier: Scalars['String']} }) + publicMarketplaceApps?: (MarketplaceAppGenqlSelection & { __args: {isVetted: Scalars['Boolean']} }) + publicMarketplaceAppDetail?: (MarketplaceAppDetailGenqlSelection & { __args: {universalIdentifier: Scalars['String']} }) field?: (FieldGenqlSelection & { __args: { /** The id of the record to find. */ id: Scalars['UUID']} }) @@ -5960,21 +5975,6 @@ export interface QueryGenqlSelection{ filter: FieldFilter} }) getViewGroups?: (ViewGroupGenqlSelection & { __args?: {viewId?: (Scalars['String'] | null)} }) getViewGroup?: (ViewGroupGenqlSelection & { __args: {id: Scalars['String']} }) - findManyApplications?: ApplicationGenqlSelection - findOneApplication?: (ApplicationGenqlSelection & { __args?: {id?: (Scalars['UUID'] | null), universalIdentifier?: (Scalars['UUID'] | null)} }) - findManyMarketplaceApps?: (MarketplaceAppGenqlSelection & { __args?: {universalIdentifiers?: (Scalars['String'][] | null)} }) - findMarketplaceAppDetail?: (MarketplaceAppDetailGenqlSelection & { __args: {universalIdentifier: Scalars['String']} }) - publicMarketplaceApps?: (MarketplaceAppGenqlSelection & { __args: {isVetted: Scalars['Boolean']} }) - publicMarketplaceAppDetail?: (MarketplaceAppDetailGenqlSelection & { __args: {universalIdentifier: Scalars['String']} }) - findApplicationRegistrationByClientId?: (PublicApplicationRegistrationGenqlSelection & { __args: {clientId: Scalars['String']} }) - findApplicationRegistrationByUniversalIdentifier?: (ApplicationRegistrationGenqlSelection & { __args: {universalIdentifier: Scalars['String']} }) - findManyApplicationRegistrations?: ApplicationRegistrationGenqlSelection - findOneApplicationRegistration?: (ApplicationRegistrationGenqlSelection & { __args: {id: Scalars['String']} }) - findApplicationRegistrationStats?: (ApplicationRegistrationStatsGenqlSelection & { __args: {id: Scalars['String']} }) - findApplicationRegistrationVariables?: (ApplicationRegistrationVariableDTOGenqlSelection & { __args: {applicationRegistrationId: Scalars['String']} }) - applicationRegistrationTarballUrl?: { __args: {id: Scalars['String']} } - findClaimableApplicationRegistration?: (ClaimableApplicationRegistrationGenqlSelection & { __args?: {sourcePackage?: (Scalars['String'] | null), universalIdentifier?: (Scalars['String'] | null)} }) - githubClaimAuthorizationUrl?: { __args: {applicationRegistrationId: Scalars['String']} } getRoles?: RoleGenqlSelection previewMessageCampaignAudience?: (CampaignAudiencePreviewDTOGenqlSelection & { __args: {input: PreviewMessageCampaignAudienceInput} }) unsubscribeTopics?: UnsubscribeTopicGenqlSelection @@ -6159,21 +6159,7 @@ export interface MutationGenqlSelection{ updateWorkspace?: (WorkspaceGenqlSelection & { __args: {data: UpdateWorkspaceInput} }) deleteCurrentWorkspace?: WorkspaceGenqlSelection checkCustomDomainValidRecords?: DomainValidRecordsGenqlSelection - createOneField?: (FieldGenqlSelection & { __args: {input: CreateOneFieldMetadataInput} }) - updateOneField?: (FieldGenqlSelection & { __args: {input: UpdateOneFieldMetadataInput} }) - deleteOneField?: (FieldGenqlSelection & { __args: {input: DeleteOneFieldInput} }) - createViewGroup?: (ViewGroupGenqlSelection & { __args: {input: CreateViewGroupInput} }) - createManyViewGroups?: (ViewGroupGenqlSelection & { __args: {inputs: CreateViewGroupInput[]} }) - updateViewGroup?: (ViewGroupGenqlSelection & { __args: {input: UpdateViewGroupInput} }) - updateManyViewGroups?: (ViewGroupGenqlSelection & { __args: {inputs: UpdateViewGroupInput[]} }) - deleteViewGroup?: (ViewGroupGenqlSelection & { __args: {input: DeleteViewGroupInput} }) - destroyViewGroup?: (ViewGroupGenqlSelection & { __args: {input: DestroyViewGroupInput} }) - /** @deprecated Use installApplication instead */ - installMarketplaceApp?: { __args: {universalIdentifier: Scalars['String'], version?: (Scalars['String'] | null)} } - installApplication?: (ApplicationGenqlSelection & { __args: {universalIdentifier: Scalars['String'], version?: (Scalars['String'] | null)} }) - updateApplication?: (ApplicationGenqlSelection & { __args: {id: Scalars['UUID'], input: UpdateApplicationInput} }) - uninstallApplication?: { __args: {universalIdentifier: Scalars['String']} } - syncMarketplaceCatalog?: boolean | number + upgradeApplication?: { __args: {appRegistrationId: Scalars['String'], targetVersion: Scalars['String']} } createApplicationRegistration?: (CreateApplicationRegistrationGenqlSelection & { __args: {input: CreateApplicationRegistrationInput} }) updateApplicationRegistration?: (ApplicationRegistrationGenqlSelection & { __args: {input: UpdateApplicationRegistrationInput} }) deleteApplicationRegistration?: { __args: {id: Scalars['String']} } @@ -6184,6 +6170,21 @@ export interface MutationGenqlSelection{ uploadAppTarball?: (ApplicationRegistrationGenqlSelection & { __args: {file: Scalars['Upload'], universalIdentifier?: (Scalars['String'] | null)} }) claimApplicationRegistrationOwnership?: (ApplicationRegistrationGenqlSelection & { __args: {applicationRegistrationId: Scalars['String']} }) transferApplicationRegistrationOwnership?: (ApplicationRegistrationGenqlSelection & { __args: {applicationRegistrationId: Scalars['String'], targetWorkspaceSubdomain: Scalars['String']} }) + /** @deprecated Use installApplication instead */ + installMarketplaceApp?: { __args: {universalIdentifier: Scalars['String'], version?: (Scalars['String'] | null)} } + installApplication?: (ApplicationGenqlSelection & { __args: {universalIdentifier: Scalars['String'], version?: (Scalars['String'] | null)} }) + updateApplication?: (ApplicationGenqlSelection & { __args: {id: Scalars['UUID'], input: UpdateApplicationInput} }) + uninstallApplication?: { __args: {universalIdentifier: Scalars['String']} } + syncMarketplaceCatalog?: boolean | number + createOneField?: (FieldGenqlSelection & { __args: {input: CreateOneFieldMetadataInput} }) + updateOneField?: (FieldGenqlSelection & { __args: {input: UpdateOneFieldMetadataInput} }) + deleteOneField?: (FieldGenqlSelection & { __args: {input: DeleteOneFieldInput} }) + createViewGroup?: (ViewGroupGenqlSelection & { __args: {input: CreateViewGroupInput} }) + createManyViewGroups?: (ViewGroupGenqlSelection & { __args: {inputs: CreateViewGroupInput[]} }) + updateViewGroup?: (ViewGroupGenqlSelection & { __args: {input: UpdateViewGroupInput} }) + updateManyViewGroups?: (ViewGroupGenqlSelection & { __args: {inputs: UpdateViewGroupInput[]} }) + deleteViewGroup?: (ViewGroupGenqlSelection & { __args: {input: DeleteViewGroupInput} }) + destroyViewGroup?: (ViewGroupGenqlSelection & { __args: {input: DestroyViewGroupInput} }) updateWorkspaceMemberRole?: (WorkspaceMemberGenqlSelection & { __args: {workspaceMemberId: Scalars['UUID'], roleId: Scalars['UUID']} }) createOneRole?: (RoleGenqlSelection & { __args: {createRoleInput: CreateRoleInput} }) updateOneRole?: (RoleGenqlSelection & { __args: {updateRoleInput: UpdateRoleInput} }) @@ -6276,7 +6277,6 @@ export interface MutationGenqlSelection{ createDevelopmentApplication?: (DevelopmentApplicationGenqlSelection & { __args: {universalIdentifier: Scalars['String'], name: Scalars['String']} }) syncApplication?: (WorkspaceMigrationGenqlSelection & { __args: {manifest: Scalars['JSON'], dryRun?: (Scalars['Boolean'] | null)} }) uploadApplicationFile?: (FileGenqlSelection & { __args: {file: Scalars['Upload'], applicationUniversalIdentifier: Scalars['String'], fileFolder: FileFolder, filePath: Scalars['String']} }) - upgradeApplication?: { __args: {appRegistrationId: Scalars['String'], targetVersion: Scalars['String']} } generateApplicationToken?: (ApplicationTokenPairGenqlSelection & { __args: {applicationId: Scalars['UUID']} }) renewApplicationToken?: (ApplicationTokenPairGenqlSelection & { __args: {applicationRefreshToken: Scalars['String']} }) __typename?: boolean | number @@ -6517,6 +6517,20 @@ displayName?: (Scalars['String'] | null)} export interface UpdateWorkspaceInput {subdomain?: (Scalars['String'] | null),customDomain?: (Scalars['String'] | null),displayName?: (Scalars['String'] | null),logo?: (Scalars['String'] | null),inviteHash?: (Scalars['String'] | null),isPublicInviteLinkEnabled?: (Scalars['Boolean'] | null),workspaceDiscoverability?: (WorkspaceDiscoverability | null),allowImpersonation?: (Scalars['Boolean'] | null),isGoogleAuthEnabled?: (Scalars['Boolean'] | null),isMicrosoftAuthEnabled?: (Scalars['Boolean'] | null),isPasswordAuthEnabled?: (Scalars['Boolean'] | null),isGoogleAuthBypassEnabled?: (Scalars['Boolean'] | null),isMicrosoftAuthBypassEnabled?: (Scalars['Boolean'] | null),isPasswordAuthBypassEnabled?: (Scalars['Boolean'] | null),defaultRoleId?: (Scalars['UUID'] | null),isTwoFactorAuthenticationEnforced?: (Scalars['Boolean'] | null),trashRetentionDays?: (Scalars['Float'] | null),eventLogRetentionDays?: (Scalars['Float'] | null),fastModel?: (Scalars['String'] | null),smartModel?: (Scalars['String'] | null),aiAdditionalInstructions?: (Scalars['String'] | null),editableProfileFields?: (Scalars['String'][] | null),enabledAiModelIds?: (Scalars['String'][] | null),useRecommendedModels?: (Scalars['Boolean'] | null),isInternalMessagesImportEnabled?: (Scalars['Boolean'] | null)} +export interface CreateApplicationRegistrationInput {name: Scalars['String'],universalIdentifier?: (Scalars['String'] | null),oAuthRedirectUris?: (Scalars['String'][] | null),oAuthScopes?: (Scalars['String'][] | null)} + +export interface UpdateApplicationRegistrationInput {id: Scalars['String'],update: UpdateApplicationRegistrationPayload} + +export interface UpdateApplicationRegistrationPayload {name?: (Scalars['String'] | null),oAuthRedirectUris?: (Scalars['String'][] | null),oAuthScopes?: (Scalars['String'][] | null),isListed?: (Scalars['Boolean'] | null),isPreInstalled?: (Scalars['Boolean'] | null),isVetted?: (Scalars['Boolean'] | null)} + +export interface CreateApplicationRegistrationVariableInput {applicationRegistrationId: Scalars['String'],key: Scalars['String'],value: Scalars['String'],description?: (Scalars['String'] | null),isSecret?: (Scalars['Boolean'] | null)} + +export interface UpdateApplicationRegistrationVariableInput {id: Scalars['String'],update: UpdateApplicationRegistrationVariablePayload} + +export interface UpdateApplicationRegistrationVariablePayload {value?: (Scalars['String'] | null),resetValue?: (Scalars['Boolean'] | null),description?: (Scalars['String'] | null)} + +export interface UpdateApplicationInput {autoUpgrade?: (Scalars['Boolean'] | null)} + export interface CreateOneFieldMetadataInput { /** The record to create */ field: CreateFieldInput} @@ -6553,20 +6567,6 @@ export interface DestroyViewGroupInput { /** The id of the view group to destroy. */ id: Scalars['UUID']} -export interface UpdateApplicationInput {autoUpgrade?: (Scalars['Boolean'] | null)} - -export interface CreateApplicationRegistrationInput {name: Scalars['String'],universalIdentifier?: (Scalars['String'] | null),oAuthRedirectUris?: (Scalars['String'][] | null),oAuthScopes?: (Scalars['String'][] | null)} - -export interface UpdateApplicationRegistrationInput {id: Scalars['String'],update: UpdateApplicationRegistrationPayload} - -export interface UpdateApplicationRegistrationPayload {name?: (Scalars['String'] | null),oAuthRedirectUris?: (Scalars['String'][] | null),oAuthScopes?: (Scalars['String'][] | null),isListed?: (Scalars['Boolean'] | null),isPreInstalled?: (Scalars['Boolean'] | null),isVetted?: (Scalars['Boolean'] | null)} - -export interface CreateApplicationRegistrationVariableInput {applicationRegistrationId: Scalars['String'],key: Scalars['String'],value: Scalars['String'],description?: (Scalars['String'] | null),isSecret?: (Scalars['Boolean'] | null)} - -export interface UpdateApplicationRegistrationVariableInput {id: Scalars['String'],update: UpdateApplicationRegistrationVariablePayload} - -export interface UpdateApplicationRegistrationVariablePayload {value?: (Scalars['String'] | null),resetValue?: (Scalars['Boolean'] | null),description?: (Scalars['String'] | null)} - export interface CreateRoleInput {id?: (Scalars['String'] | null),label: Scalars['String'],description?: (Scalars['String'] | null),icon?: (Scalars['String'] | null),canUpdateAllSettings?: (Scalars['Boolean'] | null),canAccessAllTools?: (Scalars['Boolean'] | null),canReadAllObjectRecords?: (Scalars['Boolean'] | null),canUpdateAllObjectRecords?: (Scalars['Boolean'] | null),canSoftDeleteAllObjectRecords?: (Scalars['Boolean'] | null),canDestroyAllObjectRecords?: (Scalars['Boolean'] | null),canBeAssignedToUsers?: (Scalars['Boolean'] | null),canBeAssignedToAgents?: (Scalars['Boolean'] | null),canBeAssignedToApiKeys?: (Scalars['Boolean'] | null)} export interface UpdateRoleInput {update: UpdateRolePayload, @@ -7763,102 +7763,6 @@ export interface LogicFunctionLogsInput {applicationId?: (Scalars['UUID'] | null - const Relation_possibleTypes: string[] = ['Relation'] - export const isRelation = (obj?: { __typename?: any } | null): obj is Relation => { - if (!obj?.__typename) throw new Error('__typename is missing in "isRelation"') - return Relation_possibleTypes.includes(obj.__typename) - } - - - - const IndexField_possibleTypes: string[] = ['IndexField'] - export const isIndexField = (obj?: { __typename?: any } | null): obj is IndexField => { - if (!obj?.__typename) throw new Error('__typename is missing in "isIndexField"') - return IndexField_possibleTypes.includes(obj.__typename) - } - - - - const ObjectRecordCount_possibleTypes: string[] = ['ObjectRecordCount'] - export const isObjectRecordCount = (obj?: { __typename?: any } | null): obj is ObjectRecordCount => { - if (!obj?.__typename) throw new Error('__typename is missing in "isObjectRecordCount"') - return ObjectRecordCount_possibleTypes.includes(obj.__typename) - } - - - - const SearchField_possibleTypes: string[] = ['SearchField'] - export const isSearchField = (obj?: { __typename?: any } | null): obj is SearchField => { - if (!obj?.__typename) throw new Error('__typename is missing in "isSearchField"') - return SearchField_possibleTypes.includes(obj.__typename) - } - - - - const ObjectEdge_possibleTypes: string[] = ['ObjectEdge'] - export const isObjectEdge = (obj?: { __typename?: any } | null): obj is ObjectEdge => { - if (!obj?.__typename) throw new Error('__typename is missing in "isObjectEdge"') - return ObjectEdge_possibleTypes.includes(obj.__typename) - } - - - - const PageInfo_possibleTypes: string[] = ['PageInfo'] - export const isPageInfo = (obj?: { __typename?: any } | null): obj is PageInfo => { - if (!obj?.__typename) throw new Error('__typename is missing in "isPageInfo"') - return PageInfo_possibleTypes.includes(obj.__typename) - } - - - - const ObjectConnection_possibleTypes: string[] = ['ObjectConnection'] - export const isObjectConnection = (obj?: { __typename?: any } | null): obj is ObjectConnection => { - if (!obj?.__typename) throw new Error('__typename is missing in "isObjectConnection"') - return ObjectConnection_possibleTypes.includes(obj.__typename) - } - - - - const IndexEdge_possibleTypes: string[] = ['IndexEdge'] - export const isIndexEdge = (obj?: { __typename?: any } | null): obj is IndexEdge => { - if (!obj?.__typename) throw new Error('__typename is missing in "isIndexEdge"') - return IndexEdge_possibleTypes.includes(obj.__typename) - } - - - - const ObjectIndexMetadatasConnection_possibleTypes: string[] = ['ObjectIndexMetadatasConnection'] - export const isObjectIndexMetadatasConnection = (obj?: { __typename?: any } | null): obj is ObjectIndexMetadatasConnection => { - if (!obj?.__typename) throw new Error('__typename is missing in "isObjectIndexMetadatasConnection"') - return ObjectIndexMetadatasConnection_possibleTypes.includes(obj.__typename) - } - - - - const FieldEdge_possibleTypes: string[] = ['FieldEdge'] - export const isFieldEdge = (obj?: { __typename?: any } | null): obj is FieldEdge => { - if (!obj?.__typename) throw new Error('__typename is missing in "isFieldEdge"') - return FieldEdge_possibleTypes.includes(obj.__typename) - } - - - - const ObjectFieldsConnection_possibleTypes: string[] = ['ObjectFieldsConnection'] - export const isObjectFieldsConnection = (obj?: { __typename?: any } | null): obj is ObjectFieldsConnection => { - if (!obj?.__typename) throw new Error('__typename is missing in "isObjectFieldsConnection"') - return ObjectFieldsConnection_possibleTypes.includes(obj.__typename) - } - - - - const FieldConnection_possibleTypes: string[] = ['FieldConnection'] - export const isFieldConnection = (obj?: { __typename?: any } | null): obj is FieldConnection => { - if (!obj?.__typename) throw new Error('__typename is missing in "isFieldConnection"') - return FieldConnection_possibleTypes.includes(obj.__typename) - } - - - const AppConnection_possibleTypes: string[] = ['AppConnection'] export const isAppConnection = (obj?: { __typename?: any } | null): obj is AppConnection => { if (!obj?.__typename) throw new Error('__typename is missing in "isAppConnection"') @@ -7987,6 +7891,102 @@ export interface LogicFunctionLogsInput {applicationId?: (Scalars['UUID'] | null + const Relation_possibleTypes: string[] = ['Relation'] + export const isRelation = (obj?: { __typename?: any } | null): obj is Relation => { + if (!obj?.__typename) throw new Error('__typename is missing in "isRelation"') + return Relation_possibleTypes.includes(obj.__typename) + } + + + + const IndexField_possibleTypes: string[] = ['IndexField'] + export const isIndexField = (obj?: { __typename?: any } | null): obj is IndexField => { + if (!obj?.__typename) throw new Error('__typename is missing in "isIndexField"') + return IndexField_possibleTypes.includes(obj.__typename) + } + + + + const ObjectRecordCount_possibleTypes: string[] = ['ObjectRecordCount'] + export const isObjectRecordCount = (obj?: { __typename?: any } | null): obj is ObjectRecordCount => { + if (!obj?.__typename) throw new Error('__typename is missing in "isObjectRecordCount"') + return ObjectRecordCount_possibleTypes.includes(obj.__typename) + } + + + + const SearchField_possibleTypes: string[] = ['SearchField'] + export const isSearchField = (obj?: { __typename?: any } | null): obj is SearchField => { + if (!obj?.__typename) throw new Error('__typename is missing in "isSearchField"') + return SearchField_possibleTypes.includes(obj.__typename) + } + + + + const ObjectEdge_possibleTypes: string[] = ['ObjectEdge'] + export const isObjectEdge = (obj?: { __typename?: any } | null): obj is ObjectEdge => { + if (!obj?.__typename) throw new Error('__typename is missing in "isObjectEdge"') + return ObjectEdge_possibleTypes.includes(obj.__typename) + } + + + + const PageInfo_possibleTypes: string[] = ['PageInfo'] + export const isPageInfo = (obj?: { __typename?: any } | null): obj is PageInfo => { + if (!obj?.__typename) throw new Error('__typename is missing in "isPageInfo"') + return PageInfo_possibleTypes.includes(obj.__typename) + } + + + + const ObjectConnection_possibleTypes: string[] = ['ObjectConnection'] + export const isObjectConnection = (obj?: { __typename?: any } | null): obj is ObjectConnection => { + if (!obj?.__typename) throw new Error('__typename is missing in "isObjectConnection"') + return ObjectConnection_possibleTypes.includes(obj.__typename) + } + + + + const IndexEdge_possibleTypes: string[] = ['IndexEdge'] + export const isIndexEdge = (obj?: { __typename?: any } | null): obj is IndexEdge => { + if (!obj?.__typename) throw new Error('__typename is missing in "isIndexEdge"') + return IndexEdge_possibleTypes.includes(obj.__typename) + } + + + + const ObjectIndexMetadatasConnection_possibleTypes: string[] = ['ObjectIndexMetadatasConnection'] + export const isObjectIndexMetadatasConnection = (obj?: { __typename?: any } | null): obj is ObjectIndexMetadatasConnection => { + if (!obj?.__typename) throw new Error('__typename is missing in "isObjectIndexMetadatasConnection"') + return ObjectIndexMetadatasConnection_possibleTypes.includes(obj.__typename) + } + + + + const FieldEdge_possibleTypes: string[] = ['FieldEdge'] + export const isFieldEdge = (obj?: { __typename?: any } | null): obj is FieldEdge => { + if (!obj?.__typename) throw new Error('__typename is missing in "isFieldEdge"') + return FieldEdge_possibleTypes.includes(obj.__typename) + } + + + + const ObjectFieldsConnection_possibleTypes: string[] = ['ObjectFieldsConnection'] + export const isObjectFieldsConnection = (obj?: { __typename?: any } | null): obj is ObjectFieldsConnection => { + if (!obj?.__typename) throw new Error('__typename is missing in "isObjectFieldsConnection"') + return ObjectFieldsConnection_possibleTypes.includes(obj.__typename) + } + + + + const FieldConnection_possibleTypes: string[] = ['FieldConnection'] + export const isFieldConnection = (obj?: { __typename?: any } | null): obj is FieldConnection => { + if (!obj?.__typename) throw new Error('__typename is missing in "isFieldConnection"') + return FieldConnection_possibleTypes.includes(obj.__typename) + } + + + const BillingEntitlement_possibleTypes: string[] = ['BillingEntitlement'] export const isBillingEntitlement = (obj?: { __typename?: any } | null): obj is BillingEntitlement => { if (!obj?.__typename) throw new Error('__typename is missing in "isBillingEntitlement"') diff --git a/packages/twenty-client-sdk/src/metadata/generated/types.ts b/packages/twenty-client-sdk/src/metadata/generated/types.ts index 6f621f53f7..f01348b7e0 100644 --- a/packages/twenty-client-sdk/src/metadata/generated/types.ts +++ b/packages/twenty-client-sdk/src/metadata/generated/types.ts @@ -60,7 +60,7 @@ export default { 186, 189, 201, - 214, + 218, 231, 237, 271, @@ -954,10 +954,10 @@ export default { 3 ], "relation": [ - 200 + 217 ], "morphRelations": [ - 200 + 217 ], "object": [ 47 @@ -993,7 +993,7 @@ export default { 4 ], "indexFieldMetadataList": [ - 202 + 219 ], "__typename": [ 1 @@ -1080,10 +1080,10 @@ export default { 45 ], "searchFieldMetadataList": [ - 204 + 221 ], "fields": [ - 211, + 228, { "paging": [ 48, @@ -1096,7 +1096,7 @@ export default { } ], "indexMetadatas": [ - 209, + 226, { "paging": [ 48, @@ -1880,7 +1880,7 @@ export default { 20 ], "deletedWorkspaceMembers": [ - 224 + 211 ], "hasPassword": [ 6 @@ -1892,7 +1892,7 @@ export default { 17 ], "availableWorkspaces": [ - 223 + 210 ], "__typename": [ 1 @@ -3857,181 +3857,9 @@ export default { 1 ] }, - "Relation": { - "type": [ - 201 - ], - "sourceObjectMetadata": [ - 47 - ], - "targetObjectMetadata": [ - 47 - ], - "sourceFieldMetadata": [ - 43 - ], - "targetFieldMetadata": [ - 43 - ], - "__typename": [ - 1 - ] - }, - "RelationType": {}, - "IndexField": { - "id": [ - 3 - ], - "fieldMetadataId": [ - 3 - ], - "order": [ - 12 - ], - "subFieldName": [ - 1 - ], - "createdAt": [ - 4 - ], - "updatedAt": [ - 4 - ], - "__typename": [ - 1 - ] - }, - "ObjectRecordCount": { - "objectNamePlural": [ - 1 - ], - "totalCount": [ - 21 - ], - "__typename": [ - 1 - ] - }, - "SearchField": { - "id": [ - 3 - ], - "fieldMetadataId": [ - 3 - ], - "tsVectorFieldMetadataId": [ - 3 - ], - "position": [ - 12 - ], - "createdAt": [ - 4 - ], - "updatedAt": [ - 4 - ], - "__typename": [ - 1 - ] - }, - "ObjectEdge": { - "node": [ - 47 - ], - "cursor": [ - 49 - ], - "__typename": [ - 1 - ] - }, - "PageInfo": { - "hasNextPage": [ - 6 - ], - "hasPreviousPage": [ - 6 - ], - "startCursor": [ - 49 - ], - "endCursor": [ - 49 - ], - "__typename": [ - 1 - ] - }, - "ObjectConnection": { - "pageInfo": [ - 206 - ], - "edges": [ - 205 - ], - "__typename": [ - 1 - ] - }, - "IndexEdge": { - "node": [ - 45 - ], - "cursor": [ - 49 - ], - "__typename": [ - 1 - ] - }, - "ObjectIndexMetadatasConnection": { - "pageInfo": [ - 206 - ], - "edges": [ - 208 - ], - "__typename": [ - 1 - ] - }, - "FieldEdge": { - "node": [ - 43 - ], - "cursor": [ - 49 - ], - "__typename": [ - 1 - ] - }, - "ObjectFieldsConnection": { - "pageInfo": [ - 206 - ], - "edges": [ - 210 - ], - "__typename": [ - 1 - ] - }, - "FieldConnection": { - "pageInfo": [ - 206 - ], - "edges": [ - 210 - ], - "__typename": [ - 1 - ] - }, "AppConnection": { "id": [ - 214 + 201 ], "providerName": [ 1 @@ -4126,7 +3954,7 @@ export default { 176 ], "workspace": [ - 218 + 205 ], "__typename": [ 1 @@ -4195,7 +4023,7 @@ export default { 1 ], "sso": [ - 221 + 208 ], "__typename": [ 1 @@ -4203,10 +4031,10 @@ export default { }, "AvailableWorkspaces": { "availableWorkspacesForSignIn": [ - 222 + 209 ], "availableWorkspacesForSignUp": [ - 222 + 209 ], "__typename": [ 1 @@ -4339,10 +4167,10 @@ export default { 1 ], "objectPermissions": [ - 226 + 213 ], "fieldPermissions": [ - 227 + 214 ], "__typename": [ 1 @@ -4410,7 +4238,7 @@ export default { 1 ], "roles": [ - 228 + 215 ], "manifest": [ 7 @@ -4419,6 +4247,178 @@ export default { 1 ] }, + "Relation": { + "type": [ + 218 + ], + "sourceObjectMetadata": [ + 47 + ], + "targetObjectMetadata": [ + 47 + ], + "sourceFieldMetadata": [ + 43 + ], + "targetFieldMetadata": [ + 43 + ], + "__typename": [ + 1 + ] + }, + "RelationType": {}, + "IndexField": { + "id": [ + 3 + ], + "fieldMetadataId": [ + 3 + ], + "order": [ + 12 + ], + "subFieldName": [ + 1 + ], + "createdAt": [ + 4 + ], + "updatedAt": [ + 4 + ], + "__typename": [ + 1 + ] + }, + "ObjectRecordCount": { + "objectNamePlural": [ + 1 + ], + "totalCount": [ + 21 + ], + "__typename": [ + 1 + ] + }, + "SearchField": { + "id": [ + 3 + ], + "fieldMetadataId": [ + 3 + ], + "tsVectorFieldMetadataId": [ + 3 + ], + "position": [ + 12 + ], + "createdAt": [ + 4 + ], + "updatedAt": [ + 4 + ], + "__typename": [ + 1 + ] + }, + "ObjectEdge": { + "node": [ + 47 + ], + "cursor": [ + 49 + ], + "__typename": [ + 1 + ] + }, + "PageInfo": { + "hasNextPage": [ + 6 + ], + "hasPreviousPage": [ + 6 + ], + "startCursor": [ + 49 + ], + "endCursor": [ + 49 + ], + "__typename": [ + 1 + ] + }, + "ObjectConnection": { + "pageInfo": [ + 223 + ], + "edges": [ + 222 + ], + "__typename": [ + 1 + ] + }, + "IndexEdge": { + "node": [ + 45 + ], + "cursor": [ + 49 + ], + "__typename": [ + 1 + ] + }, + "ObjectIndexMetadatasConnection": { + "pageInfo": [ + 223 + ], + "edges": [ + 225 + ], + "__typename": [ + 1 + ] + }, + "FieldEdge": { + "node": [ + 43 + ], + "cursor": [ + 49 + ], + "__typename": [ + 1 + ] + }, + "ObjectFieldsConnection": { + "pageInfo": [ + 223 + ], + "edges": [ + 227 + ], + "__typename": [ + 1 + ] + }, + "FieldConnection": { + "pageInfo": [ + 223 + ], + "edges": [ + 227 + ], + "__typename": [ + 1 + ] + }, "BillingEntitlement": { "key": [ 231 @@ -4623,7 +4623,7 @@ export default { 244 ], "availableWorkspaces": [ - 223 + 210 ], "__typename": [ 1 @@ -5720,7 +5720,7 @@ export default { }, "AgentChatThread": { "id": [ - 214 + 201 ], "title": [ 1 @@ -6312,7 +6312,7 @@ export default { } ], "objectRecordCounts": [ - 203 + 220 ], "mostlyEmptyFieldMetadataIds": [ 3, @@ -6333,7 +6333,7 @@ export default { } ], "objects": [ - 207, + 224, { "paging": [ 48, @@ -6419,95 +6419,6 @@ export default { ] } ], - "field": [ - 43, - { - "id": [ - 3, - "UUID!" - ] - } - ], - "fields": [ - 212, - { - "paging": [ - 48, - "CursorPaging!" - ], - "filter": [ - 50, - "FieldFilter!" - ] - } - ], - "getViewGroups": [ - 61, - { - "viewId": [ - 1 - ] - } - ], - "getViewGroup": [ - 61, - { - "id": [ - 1, - "String!" - ] - } - ], - "findManyApplications": [ - 54 - ], - "findOneApplication": [ - 54, - { - "id": [ - 3 - ], - "universalIdentifier": [ - 3 - ] - } - ], - "findManyMarketplaceApps": [ - 225, - { - "universalIdentifiers": [ - 1, - "[String!]" - ] - } - ], - "findMarketplaceAppDetail": [ - 229, - { - "universalIdentifier": [ - 1, - "String!" - ] - } - ], - "publicMarketplaceApps": [ - 225, - { - "isVetted": [ - 6, - "Boolean!" - ] - } - ], - "publicMarketplaceAppDetail": [ - 229, - { - "universalIdentifier": [ - 1, - "String!" - ] - } - ], "findApplicationRegistrationByClientId": [ 198, { @@ -6585,6 +6496,95 @@ export default { ] } ], + "findManyApplications": [ + 54 + ], + "findOneApplication": [ + 54, + { + "id": [ + 3 + ], + "universalIdentifier": [ + 3 + ] + } + ], + "findManyMarketplaceApps": [ + 212, + { + "universalIdentifiers": [ + 1, + "[String!]" + ] + } + ], + "findMarketplaceAppDetail": [ + 216, + { + "universalIdentifier": [ + 1, + "String!" + ] + } + ], + "publicMarketplaceApps": [ + 212, + { + "isVetted": [ + 6, + "Boolean!" + ] + } + ], + "publicMarketplaceAppDetail": [ + 216, + { + "universalIdentifier": [ + 1, + "String!" + ] + } + ], + "field": [ + 43, + { + "id": [ + 3, + "UUID!" + ] + } + ], + "fields": [ + 229, + { + "paging": [ + 48, + "CursorPaging!" + ], + "filter": [ + 50, + "FieldFilter!" + ] + } + ], + "getViewGroups": [ + 61, + { + "viewId": [ + 1 + ] + } + ], + "getViewGroup": [ + 61, + { + "id": [ + 1, + "String!" + ] + } + ], "getRoles": [ 29 ], @@ -6661,7 +6661,7 @@ export default { 336 ], "appConnections": [ - 213, + 200, { "filter": [ 343 @@ -6669,10 +6669,10 @@ export default { } ], "appConnection": [ - 213, + 200, { "id": [ - 214, + 201, "ID!" ] } @@ -6789,7 +6789,7 @@ export default { 74 ], "getSSOIdentityProviders": [ - 219 + 206 ], "eventLogs": [ 312, @@ -6936,7 +6936,7 @@ export default { }, "LogicFunctionIdInput": { "id": [ - 214 + 201 ], "__typename": [ 1 @@ -8046,141 +8046,24 @@ export default { "checkCustomDomainValidRecords": [ 233 ], - "createOneField": [ - 43, - { - "input": [ - 431, - "CreateOneFieldMetadataInput!" - ] - } - ], - "updateOneField": [ - 43, - { - "input": [ - 433, - "UpdateOneFieldMetadataInput!" - ] - } - ], - "deleteOneField": [ - 43, - { - "input": [ - 435, - "DeleteOneFieldInput!" - ] - } - ], - "createViewGroup": [ - 61, - { - "input": [ - 436, - "CreateViewGroupInput!" - ] - } - ], - "createManyViewGroups": [ - 61, - { - "inputs": [ - 436, - "[CreateViewGroupInput!]!" - ] - } - ], - "updateViewGroup": [ - 61, - { - "input": [ - 437, - "UpdateViewGroupInput!" - ] - } - ], - "updateManyViewGroups": [ - 61, - { - "inputs": [ - 437, - "[UpdateViewGroupInput!]!" - ] - } - ], - "deleteViewGroup": [ - 61, - { - "input": [ - 439, - "DeleteViewGroupInput!" - ] - } - ], - "destroyViewGroup": [ - 61, - { - "input": [ - 440, - "DestroyViewGroupInput!" - ] - } - ], - "installMarketplaceApp": [ + "upgradeApplication": [ 6, { - "universalIdentifier": [ + "appRegistrationId": [ 1, "String!" ], - "version": [ - 1 - ] - } - ], - "installApplication": [ - 54, - { - "universalIdentifier": [ - 1, - "String!" - ], - "version": [ - 1 - ] - } - ], - "updateApplication": [ - 54, - { - "id": [ - 3, - "UUID!" - ], - "input": [ - 441, - "UpdateApplicationInput!" - ] - } - ], - "uninstallApplication": [ - 6, - { - "universalIdentifier": [ + "targetVersion": [ 1, "String!" ] } ], - "syncMarketplaceCatalog": [ - 6 - ], "createApplicationRegistration": [ 197, { "input": [ - 442, + 431, "CreateApplicationRegistrationInput!" ] } @@ -8189,7 +8072,7 @@ export default { 8, { "input": [ - 443, + 432, "UpdateApplicationRegistrationInput!" ] } @@ -8216,7 +8099,7 @@ export default { 5, { "input": [ - 445, + 434, "CreateApplicationRegistrationVariableInput!" ] } @@ -8225,7 +8108,7 @@ export default { 5, { "input": [ - 446, + 435, "UpdateApplicationRegistrationVariableInput!" ] } @@ -8273,6 +8156,136 @@ export default { ] } ], + "installMarketplaceApp": [ + 6, + { + "universalIdentifier": [ + 1, + "String!" + ], + "version": [ + 1 + ] + } + ], + "installApplication": [ + 54, + { + "universalIdentifier": [ + 1, + "String!" + ], + "version": [ + 1 + ] + } + ], + "updateApplication": [ + 54, + { + "id": [ + 3, + "UUID!" + ], + "input": [ + 437, + "UpdateApplicationInput!" + ] + } + ], + "uninstallApplication": [ + 6, + { + "universalIdentifier": [ + 1, + "String!" + ] + } + ], + "syncMarketplaceCatalog": [ + 6 + ], + "createOneField": [ + 43, + { + "input": [ + 438, + "CreateOneFieldMetadataInput!" + ] + } + ], + "updateOneField": [ + 43, + { + "input": [ + 440, + "UpdateOneFieldMetadataInput!" + ] + } + ], + "deleteOneField": [ + 43, + { + "input": [ + 442, + "DeleteOneFieldInput!" + ] + } + ], + "createViewGroup": [ + 61, + { + "input": [ + 443, + "CreateViewGroupInput!" + ] + } + ], + "createManyViewGroups": [ + 61, + { + "inputs": [ + 443, + "[CreateViewGroupInput!]!" + ] + } + ], + "updateViewGroup": [ + 61, + { + "input": [ + 444, + "UpdateViewGroupInput!" + ] + } + ], + "updateManyViewGroups": [ + 61, + { + "inputs": [ + 444, + "[UpdateViewGroupInput!]!" + ] + } + ], + "deleteViewGroup": [ + 61, + { + "input": [ + 446, + "DeleteViewGroupInput!" + ] + } + ], + "destroyViewGroup": [ + 61, + { + "input": [ + 447, + "DestroyViewGroupInput!" + ] + } + ], "updateWorkspaceMemberRole": [ 20, { @@ -9072,7 +9085,7 @@ export default { } ], "resendEmailVerificationToken": [ - 215, + 202, { "email": [ 1, @@ -9085,7 +9098,7 @@ export default { } ], "createOIDCIdentityProvider": [ - 220, + 207, { "input": [ 483, @@ -9094,7 +9107,7 @@ export default { } ], "createSAMLIdentityProvider": [ - 220, + 207, { "input": [ 484, @@ -9103,7 +9116,7 @@ export default { } ], "deleteSSOIdentityProvider": [ - 216, + 203, { "input": [ 485, @@ -9112,7 +9125,7 @@ export default { } ], "editSSOIdentityProvider": [ - 217, + 204, { "input": [ 486, @@ -9309,19 +9322,6 @@ export default { ] } ], - "upgradeApplication": [ - 6, - { - "appRegistrationId": [ - 1, - "String!" - ], - "targetVersion": [ - 1, - "String!" - ] - } - ], "generateApplicationToken": [ 33, { @@ -10879,9 +10879,113 @@ export default { 1 ] }, + "CreateApplicationRegistrationInput": { + "name": [ + 1 + ], + "universalIdentifier": [ + 1 + ], + "oAuthRedirectUris": [ + 1 + ], + "oAuthScopes": [ + 1 + ], + "__typename": [ + 1 + ] + }, + "UpdateApplicationRegistrationInput": { + "id": [ + 1 + ], + "update": [ + 433 + ], + "__typename": [ + 1 + ] + }, + "UpdateApplicationRegistrationPayload": { + "name": [ + 1 + ], + "oAuthRedirectUris": [ + 1 + ], + "oAuthScopes": [ + 1 + ], + "isListed": [ + 6 + ], + "isPreInstalled": [ + 6 + ], + "isVetted": [ + 6 + ], + "__typename": [ + 1 + ] + }, + "CreateApplicationRegistrationVariableInput": { + "applicationRegistrationId": [ + 1 + ], + "key": [ + 1 + ], + "value": [ + 1 + ], + "description": [ + 1 + ], + "isSecret": [ + 6 + ], + "__typename": [ + 1 + ] + }, + "UpdateApplicationRegistrationVariableInput": { + "id": [ + 1 + ], + "update": [ + 436 + ], + "__typename": [ + 1 + ] + }, + "UpdateApplicationRegistrationVariablePayload": { + "value": [ + 1 + ], + "resetValue": [ + 6 + ], + "description": [ + 1 + ], + "__typename": [ + 1 + ] + }, + "UpdateApplicationInput": { + "autoUpgrade": [ + 6 + ], + "__typename": [ + 1 + ] + }, "CreateOneFieldMetadataInput": { "field": [ - 432 + 439 ], "__typename": [ 1 @@ -10954,7 +11058,7 @@ export default { 3 ], "update": [ - 434 + 441 ], "__typename": [ 1 @@ -11049,7 +11153,7 @@ export default { 3 ], "update": [ - 438 + 445 ], "__typename": [ 1 @@ -11088,110 +11192,6 @@ export default { 1 ] }, - "UpdateApplicationInput": { - "autoUpgrade": [ - 6 - ], - "__typename": [ - 1 - ] - }, - "CreateApplicationRegistrationInput": { - "name": [ - 1 - ], - "universalIdentifier": [ - 1 - ], - "oAuthRedirectUris": [ - 1 - ], - "oAuthScopes": [ - 1 - ], - "__typename": [ - 1 - ] - }, - "UpdateApplicationRegistrationInput": { - "id": [ - 1 - ], - "update": [ - 444 - ], - "__typename": [ - 1 - ] - }, - "UpdateApplicationRegistrationPayload": { - "name": [ - 1 - ], - "oAuthRedirectUris": [ - 1 - ], - "oAuthScopes": [ - 1 - ], - "isListed": [ - 6 - ], - "isPreInstalled": [ - 6 - ], - "isVetted": [ - 6 - ], - "__typename": [ - 1 - ] - }, - "CreateApplicationRegistrationVariableInput": { - "applicationRegistrationId": [ - 1 - ], - "key": [ - 1 - ], - "value": [ - 1 - ], - "description": [ - 1 - ], - "isSecret": [ - 6 - ], - "__typename": [ - 1 - ] - }, - "UpdateApplicationRegistrationVariableInput": { - "id": [ - 1 - ], - "update": [ - 447 - ], - "__typename": [ - 1 - ] - }, - "UpdateApplicationRegistrationVariablePayload": { - "value": [ - 1 - ], - "resetValue": [ - 6 - ], - "description": [ - 1 - ], - "__typename": [ - 1 - ] - }, "CreateRoleInput": { "id": [ 1 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 becda88783..ce654a9d12 100644 --- a/packages/twenty-docs/developers/extend/apps/data/system-fields.mdx +++ b/packages/twenty-docs/developers/extend/apps/data/system-fields.mdx @@ -86,6 +86,52 @@ The same resolved id works anywhere a `fieldMetadataUniversalIdentifier` is expe inputs change, and avoids drift if the derivation ever evolves. +## System relation fields + + + `getSystemRelationFieldUniversalIdentifier` is available from `twenty-sdk` + 2.23 onward and requires a Twenty server on 2.23 or later. + + +Besides the scalar system fields above, the server also provisions four **system relation fields** on every object: `timelineActivities`, `attachments`, `noteTargets` and `taskTargets`, each pointing at the matching standard relation object. + +These fields are not resolved with `getFieldUniversalIdentifier`: their identifier is derived **name-free**, from the object hosting the field and the object the field points to. That way renaming an object never changes the identifiers of its relation fields. + +Use `getSystemRelationFieldUniversalIdentifier` to resolve them: + +```ts +import { + getSystemRelationFieldUniversalIdentifier, + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS, +} from 'twenty-sdk/define'; + +// rocket.attachments — the relation field hosted on your custom object +const rocketAttachmentsFieldId = getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: ROCKET_OBJECT_UNIVERSAL_IDENTIFIER, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.attachment.universalIdentifier, +}); +``` + +- `objectUniversalIdentifier` is the object **hosting** the field. +- `relationTargetObjectUniversalIdentifier` is the object the field **points to**. + +The direction is encoded by the argument order. To resolve the reverse side (e.g. `attachment.targetRocket`, the morph field the server creates on the standard relation object), swap the two: + +```ts +// attachment.targetRocket — the reverse morph field on Attachment +const attachmentTargetRocketFieldId = + getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.attachment.universalIdentifier, + relationTargetObjectUniversalIdentifier: ROCKET_OBJECT_UNIVERSAL_IDENTIFIER, + }); +``` + +As with scalar system fields, the resolved id works anywhere a `fieldMetadataUniversalIdentifier` is expected. + ## 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. diff --git a/packages/twenty-docs/developers/extend/apps/operations/sync-and-recovery.mdx b/packages/twenty-docs/developers/extend/apps/operations/sync-and-recovery.mdx index 66ece93b59..b6054081f0 100644 --- a/packages/twenty-docs/developers/extend/apps/operations/sync-and-recovery.mdx +++ b/packages/twenty-docs/developers/extend/apps/operations/sync-and-recovery.mdx @@ -73,7 +73,7 @@ yarn twenty plan Building manifest... Computing metadata plan (read-only, nothing will be applied)... - # fieldMetadata "timelineActivities" will be created + # fieldMetadata "crewCapacity" will be created + ... Plan: 1 to add, 1 to change, 0 to destroy. diff --git a/packages/twenty-front/src/testing/mock-data/generated/metadata/objects/mock-objects-metadata.ts b/packages/twenty-front/src/testing/mock-data/generated/metadata/objects/mock-objects-metadata.ts index b738d23c46..512191e494 100644 --- a/packages/twenty-front/src/testing/mock-data/generated/metadata/objects/mock-objects-metadata.ts +++ b/packages/twenty-front/src/testing/mock-data/generated/metadata/objects/mock-objects-metadata.ts @@ -1388,7 +1388,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = { "__typename": "Field", "id": "44d1bf28-de4c-46c8-a7a5-7ba770db12f2", - "universalIdentifier": "20202020-bf6f-4220-8c55-2764f1175870", + "universalIdentifier": "6e0748cd-8876-53f9-b78d-8bdb3c71883e", "type": "RELATION", "name": "attachments", "label": "Attachments", @@ -1440,7 +1440,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = { "__typename": "Field", "id": "20acfd6a-3b43-4829-8844-2fb9539ed78b", - "universalIdentifier": "99c330c0-5b7d-4276-a764-aed84499dfb5", + "universalIdentifier": "82711ab6-00a2-57e7-9db8-9f77dde9944c", "type": "RELATION", "name": "timelineActivities", "label": "Timeline Activities", @@ -1899,7 +1899,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = { "__typename": "Field", "id": "24084abc-ee9c-49e2-aff3-410c35234d0e", - "universalIdentifier": "20202020-87c7-4118-83d6-2f4031005209", + "universalIdentifier": "37d15d4b-7441-5bbc-8a5f-228f2d62abf4", "type": "RELATION", "name": "attachments", "label": "Attachments", @@ -2005,7 +2005,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = { "__typename": "Field", "id": "8793e14d-8782-48c4-93f0-d665f48215bb", - "universalIdentifier": "20202020-dd3f-42d5-a382-db58aabf43d3", + "universalIdentifier": "ee980fc9-5f51-5986-9fbe-ccf5a103f595", "type": "RELATION", "name": "noteTargets", "label": "Notes", @@ -2111,7 +2111,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = { "__typename": "Field", "id": "5925f4d7-f2ea-4f84-9fe8-8cf337b938d0", - "universalIdentifier": "20202020-59c0-4179-a208-4a255f04a5be", + "universalIdentifier": "3e20f0e5-c0f5-5af4-8cac-02a65ecc4e86", "type": "RELATION", "name": "taskTargets", "label": "Tasks", @@ -2163,7 +2163,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = { "__typename": "Field", "id": "a8a81fec-6315-46b1-927b-064f3e52ab94", - "universalIdentifier": "20202020-30e2-421f-96c7-19c69d1cf631", + "universalIdentifier": "64f26c76-9427-55a1-82e6-332b495878ad", "type": "RELATION", "name": "timelineActivities", "label": "Timeline Activities", @@ -3185,7 +3185,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = { "__typename": "Field", "id": "0419db16-5fd4-4306-bd1d-ea0b1dde5349", - "universalIdentifier": "20202020-7664-4a35-a3df-580d389fd527", + "universalIdentifier": "1598c13c-d539-52c8-a56c-974a6b724393", "type": "MORPH_RELATION", "name": "target", "label": "Target", @@ -4136,7 +4136,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = { "__typename": "Field", "id": "077d02f7-6730-40dd-a9bb-5960a68fe033", - "universalIdentifier": "20202020-4a8c-4e2d-9b1c-7e5f3a2b4c6d", + "universalIdentifier": "78d27c16-be35-54d2-988a-451dc24f77ff", "type": "RELATION", "name": "attachments", "label": "Attachments", @@ -4188,7 +4188,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = { "__typename": "Field", "id": "5bfdccb5-ae15-428e-9cb8-acc2569220dc", - "universalIdentifier": "20202020-906e-486a-a798-131a5f081faf", + "universalIdentifier": "0ca041cd-598d-5c6b-881c-fe89ee97a0bc", "type": "RELATION", "name": "timelineActivities", "label": "Timeline Activities", @@ -10001,7 +10001,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = { "__typename": "Field", "id": "3eaf07bb-8dae-42d8-870f-8499499cb790", - "universalIdentifier": "20202020-af4d-4eb0-babc-eb960a45b356", + "universalIdentifier": "1e8e4e87-48c3-54d5-bd75-5cda9cf393b0", "type": "RELATION", "name": "timelineActivities", "label": "Timeline Activities", @@ -13956,7 +13956,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = { "__typename": "Field", "id": "13d4bb57-4d6e-43ec-a9ed-1e319af50cd8", - "universalIdentifier": "20202020-794d-4783-a8ff-cecdb15be139", + "universalIdentifier": "d88469f5-b558-5e11-ab85-66f9ffcaecc0", "type": "RELATION", "name": "attachments", "label": "Attachments", @@ -14114,7 +14114,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = { "__typename": "Field", "id": "4bd3102c-f570-4488-abb6-1cac4889a8a6", - "universalIdentifier": "20202020-c778-4278-99ee-23a2837aee64", + "universalIdentifier": "9058db0a-b156-5c9e-999f-c89387ef4a9b", "type": "RELATION", "name": "timelineActivities", "label": "Timeline Activities", @@ -14587,7 +14587,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = { "__typename": "Field", "id": "1a1ee54b-8af0-46f1-b02e-836dc676cd53", - "universalIdentifier": "20202020-fcb0-4695-b17e-3b43a421c633", + "universalIdentifier": "4be90325-f6ca-5931-a977-1194bb4b3d5c", "type": "RELATION", "name": "timelineActivities", "label": "Timeline Activities", @@ -15279,7 +15279,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = { "__typename": "Field", "id": "60cfa81c-51ae-41f2-96cc-ee75ae1d57c4", - "universalIdentifier": "20202020-cd97-451f-87fa-bcb789bdbf3a", + "universalIdentifier": "54dd92e1-67bf-5381-86f3-c747976dcdaf", "type": "RELATION", "name": "attachments", "label": "Attachments", @@ -15489,7 +15489,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = { "__typename": "Field", "id": "2a4ae297-c70c-4c18-978a-b1dfbf472e8f", - "universalIdentifier": "20202020-c8fc-4258-8250-15905d3fcfec", + "universalIdentifier": "1c0e0dfe-a01f-58d6-8948-08b93164d03c", "type": "RELATION", "name": "noteTargets", "label": "Notes", @@ -15593,7 +15593,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = { "__typename": "Field", "id": "d97cf1e1-e92c-4ec0-b349-7a8a2efc4d83", - "universalIdentifier": "20202020-584b-4d3e-88b6-53ab1fa03c3a", + "universalIdentifier": "ddea86e6-5766-5798-ac32-14fbab6089f6", "type": "RELATION", "name": "taskTargets", "label": "Tasks", @@ -15645,7 +15645,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = { "__typename": "Field", "id": "f2839b7a-3eae-4048-9b00-4a631f733e70", - "universalIdentifier": "20202020-a43e-4873-9c23-e522de906ce5", + "universalIdentifier": "32d7483f-6227-528b-bc65-62d9cf858cd8", "type": "RELATION", "name": "timelineActivities", "label": "Events", @@ -17109,7 +17109,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = { "__typename": "Field", "id": "7bea72b6-41ff-4012-83fe-df7fd52e5ffc", - "universalIdentifier": "20202020-c1b5-4120-b0f0-987ca401ed53", + "universalIdentifier": "a4399ae3-fe89-59fe-a282-aec77d4e63a8", "type": "RELATION", "name": "attachments", "label": "Attachments", @@ -17267,7 +17267,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = { "__typename": "Field", "id": "ef490149-f4f3-437e-a40a-7b1528b6b09a", - "universalIdentifier": "20202020-cb17-4a61-8f8f-3be6730480de", + "universalIdentifier": "9d7b0087-7037-55dc-bb59-456064455694", "type": "RELATION", "name": "taskTargets", "label": "Tasks", @@ -17319,7 +17319,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = { "__typename": "Field", "id": "e34f74fc-3019-4c01-a23d-a9312444a7aa", - "universalIdentifier": "20202020-bae0-4556-a74a-a9c686f77a88", + "universalIdentifier": "0b28db77-8099-5ce3-a11f-86a5778d7740", "type": "RELATION", "name": "noteTargets", "label": "Notes", @@ -17423,7 +17423,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = { "__typename": "Field", "id": "3b6d99ee-d1f8-4011-b05e-9da8f1d21fe8", - "universalIdentifier": "20202020-0414-4daf-9c0d-64fe7b27f89f", + "universalIdentifier": "28ec0f8f-ad23-5317-b2d9-1a487bcefee2", "type": "RELATION", "name": "timelineActivities", "label": "Timeline Activities", @@ -18066,7 +18066,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = { "__typename": "Field", "id": "471b32ff-a033-4085-95c3-ef240b47f00e", - "universalIdentifier": "20202020-4986-4c92-bf19-39934b149b16", + "universalIdentifier": "b5e03b44-f726-5eae-9ea9-141d074f2120", "type": "RELATION", "name": "attachments", "label": "Attachments", @@ -18170,7 +18170,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = { "__typename": "Field", "id": "583a87e2-b8ea-4a7f-bbe0-deded7e5f3db", - "universalIdentifier": "20202020-7030-42f8-929c-1a57b25d6bce", + "universalIdentifier": "dd80c0fb-074a-5ca2-8596-dbb967f20155", "type": "RELATION", "name": "timelineActivities", "label": "Timeline Activities", diff --git a/packages/twenty-front/src/testing/mock-data/workflow-run.ts b/packages/twenty-front/src/testing/mock-data/workflow-run.ts index c164b4d7ce..4083b736f9 100644 --- a/packages/twenty-front/src/testing/mock-data/workflow-run.ts +++ b/packages/twenty-front/src/testing/mock-data/workflow-run.ts @@ -867,7 +867,7 @@ export const oneSucceededWorkflowRunQueryResult = { createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', isNullable: true, - standardId: '20202020-cb17-4a61-8f8f-3be6730480de', + standardId: '9d7b0087-7037-55dc-bb59-456064455694', description: 'Tasks tied to the company', workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419', defaultValue: null, @@ -892,7 +892,7 @@ export const oneSucceededWorkflowRunQueryResult = { createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', isNullable: true, - standardId: '20202020-bae0-4556-a74a-a9c686f77a88', + standardId: '0b28db77-8099-5ce3-a11f-86a5778d7740', description: 'Notes tied to the company', workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419', defaultValue: null, @@ -967,7 +967,7 @@ export const oneSucceededWorkflowRunQueryResult = { createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', isNullable: true, - standardId: '20202020-c1b5-4120-b0f0-987ca401ed53', + standardId: 'a4399ae3-fe89-59fe-a282-aec77d4e63a8', description: 'Attachments linked to the company', workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419', defaultValue: null, @@ -992,7 +992,7 @@ export const oneSucceededWorkflowRunQueryResult = { createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', isNullable: true, - standardId: '20202020-0414-4daf-9c0d-64fe7b27f89f', + standardId: '28ec0f8f-ad23-5317-b2d9-1a487bcefee2', description: 'Timeline Activities linked to the company', workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419', @@ -2048,7 +2048,7 @@ export const oneSucceededWorkflowRunQueryResult = { createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', isNullable: true, - standardId: '20202020-cb17-4a61-8f8f-3be6730480de', + standardId: '9d7b0087-7037-55dc-bb59-456064455694', description: 'Tasks tied to the company', workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419', defaultValue: null, @@ -2086,7 +2086,7 @@ export const oneSucceededWorkflowRunQueryResult = { createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', isNullable: true, - standardId: '20202020-bae0-4556-a74a-a9c686f77a88', + standardId: '0b28db77-8099-5ce3-a11f-86a5778d7740', description: 'Notes tied to the company', workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419', defaultValue: null, @@ -2200,7 +2200,7 @@ export const oneSucceededWorkflowRunQueryResult = { createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', isNullable: true, - standardId: '20202020-c1b5-4120-b0f0-987ca401ed53', + standardId: 'a4399ae3-fe89-59fe-a282-aec77d4e63a8', description: 'Attachments linked to the company', workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419', defaultValue: null, @@ -2238,7 +2238,7 @@ export const oneSucceededWorkflowRunQueryResult = { createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', isNullable: true, - standardId: '20202020-0414-4daf-9c0d-64fe7b27f89f', + standardId: '28ec0f8f-ad23-5317-b2d9-1a487bcefee2', description: 'Timeline Activities linked to the company', workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419', defaultValue: null, @@ -2528,7 +2528,7 @@ export const oneSucceededWorkflowRunQueryResult = { createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', isNullable: true, - standardId: '20202020-0414-4daf-9c0d-64fe7b27f89f', + standardId: '28ec0f8f-ad23-5317-b2d9-1a487bcefee2', description: 'Timeline Activities linked to the company', workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419', defaultValue: null, @@ -2566,7 +2566,7 @@ export const oneSucceededWorkflowRunQueryResult = { createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', isNullable: true, - standardId: '20202020-c1b5-4120-b0f0-987ca401ed53', + standardId: 'a4399ae3-fe89-59fe-a282-aec77d4e63a8', description: 'Attachments linked to the company', workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419', defaultValue: null, @@ -3041,7 +3041,7 @@ export const oneSucceededWorkflowRunQueryResult = { createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', isNullable: true, - standardId: '20202020-bae0-4556-a74a-a9c686f77a88', + standardId: '0b28db77-8099-5ce3-a11f-86a5778d7740', description: 'Notes tied to the company', workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419', defaultValue: null, @@ -3266,7 +3266,7 @@ export const oneSucceededWorkflowRunQueryResult = { createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', isNullable: true, - standardId: '20202020-cb17-4a61-8f8f-3be6730480de', + standardId: '9d7b0087-7037-55dc-bb59-456064455694', description: 'Tasks tied to the company', workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419', defaultValue: null, @@ -3918,7 +3918,7 @@ export const oneSucceededWorkflowRunQueryResult = { createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', isNullable: true, - standardId: '20202020-c1b5-4120-b0f0-987ca401ed53', + standardId: 'a4399ae3-fe89-59fe-a282-aec77d4e63a8', description: 'Attachments linked to the company', workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419', defaultValue: null, @@ -3956,7 +3956,7 @@ export const oneSucceededWorkflowRunQueryResult = { createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', isNullable: true, - standardId: '20202020-bae0-4556-a74a-a9c686f77a88', + standardId: '0b28db77-8099-5ce3-a11f-86a5778d7740', description: 'Notes tied to the company', workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419', defaultValue: null, @@ -3994,7 +3994,7 @@ export const oneSucceededWorkflowRunQueryResult = { createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', isNullable: true, - standardId: '20202020-cb17-4a61-8f8f-3be6730480de', + standardId: '9d7b0087-7037-55dc-bb59-456064455694', description: 'Tasks tied to the company', workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419', defaultValue: null, @@ -4218,7 +4218,7 @@ export const oneSucceededWorkflowRunQueryResult = { createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', isNullable: true, - standardId: '20202020-0414-4daf-9c0d-64fe7b27f89f', + standardId: '28ec0f8f-ad23-5317-b2d9-1a487bcefee2', description: 'Timeline Activities linked to the company', workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419', defaultValue: null, @@ -5316,7 +5316,7 @@ export const oneSucceededWorkflowRunQueryResult = { createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', isNullable: true, - standardId: '20202020-cb17-4a61-8f8f-3be6730480de', + standardId: '9d7b0087-7037-55dc-bb59-456064455694', description: 'Tasks tied to the company', workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419', defaultValue: null, @@ -5340,7 +5340,7 @@ export const oneSucceededWorkflowRunQueryResult = { createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', isNullable: true, - standardId: '20202020-bae0-4556-a74a-a9c686f77a88', + standardId: '0b28db77-8099-5ce3-a11f-86a5778d7740', description: 'Notes tied to the company', workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419', defaultValue: null, @@ -5412,7 +5412,7 @@ export const oneSucceededWorkflowRunQueryResult = { createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', isNullable: true, - standardId: '20202020-c1b5-4120-b0f0-987ca401ed53', + standardId: 'a4399ae3-fe89-59fe-a282-aec77d4e63a8', description: 'Attachments linked to the company', workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419', defaultValue: null, @@ -5436,7 +5436,7 @@ export const oneSucceededWorkflowRunQueryResult = { createdAt: '2025-03-05T15:41:09.390Z', updatedAt: '2025-03-05T15:41:09.390Z', isNullable: true, - standardId: '20202020-0414-4daf-9c0d-64fe7b27f89f', + standardId: '28ec0f8f-ad23-5317-b2d9-1a487bcefee2', description: 'Timeline Activities linked to the company', workspaceId: '20202020-1c25-4d02-bf25-6aeccf7ea419', defaultValue: null, diff --git a/packages/twenty-front/src/testing/mock-data/workflow.ts b/packages/twenty-front/src/testing/mock-data/workflow.ts index ee3469ec51..2825471ab2 100644 --- a/packages/twenty-front/src/testing/mock-data/workflow.ts +++ b/packages/twenty-front/src/testing/mock-data/workflow.ts @@ -1150,7 +1150,7 @@ export const workflowQueryResult = { updatedAt: '2025-01-30T09:40:10.256Z', isNullable: true, standardId: - '20202020-cb17-4a61-8f8f-3be6730480de', + '9d7b0087-7037-55dc-bb59-456064455694', description: 'Tasks tied to the company', workspaceId: '3b8e6458-5fc1-4e63-8563-008ccddaa6db', @@ -1177,7 +1177,7 @@ export const workflowQueryResult = { updatedAt: '2025-01-30T09:40:10.256Z', isNullable: true, standardId: - '20202020-bae0-4556-a74a-a9c686f77a88', + '0b28db77-8099-5ce3-a11f-86a5778d7740', description: 'Notes tied to the company', workspaceId: '3b8e6458-5fc1-4e63-8563-008ccddaa6db', @@ -1260,7 +1260,7 @@ export const workflowQueryResult = { updatedAt: '2025-01-30T09:40:10.256Z', isNullable: true, standardId: - '20202020-c1b5-4120-b0f0-987ca401ed53', + 'a4399ae3-fe89-59fe-a282-aec77d4e63a8', description: 'Attachments linked to the company', workspaceId: @@ -1288,7 +1288,7 @@ export const workflowQueryResult = { updatedAt: '2025-01-30T09:40:10.256Z', isNullable: true, standardId: - '20202020-0414-4daf-9c0d-64fe7b27f89f', + '28ec0f8f-ad23-5317-b2d9-1a487bcefee2', description: 'Timeline Activities linked to the company', workspaceId: diff --git a/packages/twenty-sdk/src/cli/__tests__/apps/minimal-app/__integration__/app-dev/expected-manifest.ts b/packages/twenty-sdk/src/cli/__tests__/apps/minimal-app/__integration__/app-dev/expected-manifest.ts index 02687cc108..e589b603b9 100644 --- a/packages/twenty-sdk/src/cli/__tests__/apps/minimal-app/__integration__/app-dev/expected-manifest.ts +++ b/packages/twenty-sdk/src/cli/__tests__/apps/minimal-app/__integration__/app-dev/expected-manifest.ts @@ -1,10 +1,6 @@ import { FieldType } from '@/sdk/define'; import type { Manifest } from 'twenty-shared/application'; -import { - FieldMetadataType, - RelationOnDeleteAction, - RelationType, -} from 'twenty-shared/types'; +import { FieldMetadataType } from 'twenty-shared/types'; export const EXPECTED_MANIFEST: Manifest = { commandMenuItems: [], @@ -23,88 +19,7 @@ export const EXPECTED_MANIFEST: Manifest = { agents: [], publicAssets: [], indexes: [], - fields: [ - { - name: 'targetMyNote', - label: 'MyNote', - description: 'MyNote My note', - icon: 'IconTimelineEvent', - isNullable: true, - universalSettings: { - relationType: RelationType.MANY_TO_ONE, - onDelete: RelationOnDeleteAction.SET_NULL, - joinColumnName: 'targetMyNoteId', - }, - universalIdentifier: '55153e55-1bdc-590d-8fb1-40eafec88ef0', - objectUniversalIdentifier: '20202020-6736-4337-b5c4-8b39fae325a5', - relationTargetFieldMetadataUniversalIdentifier: - '559713af-d9c0-5894-a972-9a3951661a2b', - relationTargetObjectMetadataUniversalIdentifier: - 'e1e2e3e4-e5e6-4000-8000-000000000030', - type: FieldMetadataType.MORPH_RELATION, - morphId: '20202020-9a2b-4c3d-a4e5-f6a7b8c9d0e1', - }, - { - name: 'targetMyNote', - label: 'MyNote', - description: 'MyNote My note', - icon: 'IconFileImport', - isNullable: true, - universalSettings: { - relationType: RelationType.MANY_TO_ONE, - onDelete: RelationOnDeleteAction.SET_NULL, - joinColumnName: 'targetMyNoteId', - }, - universalIdentifier: '80596d88-f896-5108-8cff-302a2eb61668', - objectUniversalIdentifier: '20202020-bd3d-4c60-8dca-571c71d4447a', - relationTargetFieldMetadataUniversalIdentifier: - '8f8e42e3-77de-5b0a-b444-d96b6d3c7600', - relationTargetObjectMetadataUniversalIdentifier: - 'e1e2e3e4-e5e6-4000-8000-000000000030', - type: FieldMetadataType.MORPH_RELATION, - morphId: '20202020-f634-435d-ab8d-e1168b375c69', - }, - { - name: 'targetMyNote', - label: 'MyNote', - description: 'MyNote My note', - icon: 'IconCheckbox', - isNullable: true, - universalSettings: { - relationType: RelationType.MANY_TO_ONE, - onDelete: RelationOnDeleteAction.SET_NULL, - joinColumnName: 'targetMyNoteId', - }, - universalIdentifier: '67bf90bd-f3d0-58ce-a96e-648364249812', - objectUniversalIdentifier: '20202020-fff0-4b44-be82-bda313884400', - relationTargetFieldMetadataUniversalIdentifier: - '0cbc9a5c-34b2-5d39-97a2-878662cb13d5', - relationTargetObjectMetadataUniversalIdentifier: - 'e1e2e3e4-e5e6-4000-8000-000000000030', - type: FieldMetadataType.MORPH_RELATION, - morphId: '20202020-f635-435d-ab8d-e1168b375c70', - }, - { - name: 'targetMyNote', - label: 'MyNote', - description: 'MyNote My note', - icon: 'IconCheckbox', - isNullable: true, - universalSettings: { - relationType: RelationType.MANY_TO_ONE, - onDelete: RelationOnDeleteAction.SET_NULL, - joinColumnName: 'targetMyNoteId', - }, - universalIdentifier: '2689365b-e6ce-5b14-a2b5-6daafbfe91f8', - objectUniversalIdentifier: '20202020-5a9a-44e8-95df-771cd06d0fb1', - relationTargetFieldMetadataUniversalIdentifier: - 'affdd37c-4460-53fc-998b-ffb04bb3a470', - relationTargetObjectMetadataUniversalIdentifier: - 'e1e2e3e4-e5e6-4000-8000-000000000030', - type: FieldMetadataType.MORPH_RELATION, - morphId: '20202020-f636-435d-ab8d-e1168b375c71', - }, - ], + fields: [], objects: [ { universalIdentifier: 'e1e2e3e4-e5e6-4000-8000-000000000030', @@ -133,70 +48,6 @@ export const EXPECTED_MANIFEST: Manifest = { type: FieldMetadataType.TEXT, universalIdentifier: '9a7f457a-6c15-581e-8f28-e2c680f75f97', }, - { - name: 'timelineActivities', - label: 'Timeline Activities', - description: 'My notes tied to the MyNote', - icon: 'IconBuildingSkyscraper', - isNullable: true, - type: FieldType.RELATION, - universalSettings: { - relationType: RelationType.ONE_TO_MANY, - }, - universalIdentifier: '559713af-d9c0-5894-a972-9a3951661a2b', - relationTargetFieldMetadataUniversalIdentifier: - '55153e55-1bdc-590d-8fb1-40eafec88ef0', - relationTargetObjectMetadataUniversalIdentifier: - '20202020-6736-4337-b5c4-8b39fae325a5', - }, - { - name: 'attachments', - label: 'Attachments', - description: 'My notes tied to the MyNote', - icon: 'IconBuildingSkyscraper', - isNullable: true, - type: FieldType.RELATION, - universalSettings: { - relationType: RelationType.ONE_TO_MANY, - }, - universalIdentifier: '8f8e42e3-77de-5b0a-b444-d96b6d3c7600', - relationTargetFieldMetadataUniversalIdentifier: - '80596d88-f896-5108-8cff-302a2eb61668', - relationTargetObjectMetadataUniversalIdentifier: - '20202020-bd3d-4c60-8dca-571c71d4447a', - }, - { - name: 'noteTargets', - label: 'Note Targets', - description: 'My notes tied to the MyNote', - icon: 'IconBuildingSkyscraper', - isNullable: true, - type: FieldType.RELATION, - universalSettings: { - relationType: RelationType.ONE_TO_MANY, - }, - universalIdentifier: '0cbc9a5c-34b2-5d39-97a2-878662cb13d5', - relationTargetFieldMetadataUniversalIdentifier: - '67bf90bd-f3d0-58ce-a96e-648364249812', - relationTargetObjectMetadataUniversalIdentifier: - '20202020-fff0-4b44-be82-bda313884400', - }, - { - name: 'taskTargets', - label: 'Task Targets', - description: 'My notes tied to the MyNote', - icon: 'IconBuildingSkyscraper', - isNullable: true, - type: FieldType.RELATION, - universalSettings: { - relationType: RelationType.ONE_TO_MANY, - }, - universalIdentifier: 'affdd37c-4460-53fc-998b-ffb04bb3a470', - relationTargetFieldMetadataUniversalIdentifier: - '2689365b-e6ce-5b14-a2b5-6daafbfe91f8', - relationTargetObjectMetadataUniversalIdentifier: - '20202020-5a9a-44e8-95df-771cd06d0fb1', - }, ], }, ], diff --git a/packages/twenty-sdk/src/cli/__tests__/apps/rich-app/__integration__/app-dev/expected-manifest.ts b/packages/twenty-sdk/src/cli/__tests__/apps/rich-app/__integration__/app-dev/expected-manifest.ts index 1ad0594ebf..825bc55342 100644 --- a/packages/twenty-sdk/src/cli/__tests__/apps/rich-app/__integration__/app-dev/expected-manifest.ts +++ b/packages/twenty-sdk/src/cli/__tests__/apps/rich-app/__integration__/app-dev/expected-manifest.ts @@ -297,87 +297,6 @@ export const EXPECTED_MANIFEST: Manifest = { ], fields: [ - // Reverse relation fields for rootNote (5 fields) - { - description: 'RootNote Root note', - icon: 'IconTimelineEvent', - isNullable: true, - label: 'RootNote', - morphId: '20202020-9a2b-4c3d-a4e5-f6a7b8c9d0e1', - name: 'targetRootNote', - objectUniversalIdentifier: '20202020-6736-4337-b5c4-8b39fae325a5', - relationTargetFieldMetadataUniversalIdentifier: - 'e1478d59-c947-5926-af8b-4b2f0f842c91', - relationTargetObjectMetadataUniversalIdentifier: - 'b0b1b2b3-b4b5-4000-8000-000000000001', - type: FieldType.MORPH_RELATION, - universalIdentifier: '7f891268-9376-5d00-b8ec-bc32a023154d', - universalSettings: { - joinColumnName: 'targetRootNoteId', - onDelete: RelationOnDeleteAction.SET_NULL, - relationType: RelationType.MANY_TO_ONE, - }, - }, - { - description: 'RootNote Root note', - icon: 'IconFileImport', - isNullable: true, - label: 'RootNote', - morphId: '20202020-f634-435d-ab8d-e1168b375c69', - name: 'targetRootNote', - objectUniversalIdentifier: '20202020-bd3d-4c60-8dca-571c71d4447a', - relationTargetFieldMetadataUniversalIdentifier: - '34a6b8e9-0187-57e1-86c1-41bacf1b2479', - relationTargetObjectMetadataUniversalIdentifier: - 'b0b1b2b3-b4b5-4000-8000-000000000001', - type: FieldType.MORPH_RELATION, - universalIdentifier: 'd72bb002-9952-5651-983e-649436949113', - universalSettings: { - joinColumnName: 'targetRootNoteId', - onDelete: RelationOnDeleteAction.SET_NULL, - relationType: RelationType.MANY_TO_ONE, - }, - }, - { - description: 'RootNote Root note', - icon: 'IconCheckbox', - isNullable: true, - label: 'RootNote', - morphId: '20202020-f635-435d-ab8d-e1168b375c70', - name: 'targetRootNote', - objectUniversalIdentifier: '20202020-fff0-4b44-be82-bda313884400', - relationTargetFieldMetadataUniversalIdentifier: - '7a62bbd6-e697-54b0-8c1c-163ac47c0305', - relationTargetObjectMetadataUniversalIdentifier: - 'b0b1b2b3-b4b5-4000-8000-000000000001', - type: FieldType.MORPH_RELATION, - universalIdentifier: '61252ad6-2311-5054-ae51-b318dfd5fbd8', - universalSettings: { - joinColumnName: 'targetRootNoteId', - onDelete: RelationOnDeleteAction.SET_NULL, - relationType: RelationType.MANY_TO_ONE, - }, - }, - { - description: 'RootNote Root note', - icon: 'IconCheckbox', - isNullable: true, - label: 'RootNote', - morphId: '20202020-f636-435d-ab8d-e1168b375c71', - name: 'targetRootNote', - objectUniversalIdentifier: '20202020-5a9a-44e8-95df-771cd06d0fb1', - relationTargetFieldMetadataUniversalIdentifier: - '10c1af7e-e541-56bc-b5b3-d0297df3e23d', - relationTargetObjectMetadataUniversalIdentifier: - 'b0b1b2b3-b4b5-4000-8000-000000000001', - type: FieldType.MORPH_RELATION, - universalIdentifier: '2f4f3d69-b78e-5465-9e87-17059959fca8', - universalSettings: { - joinColumnName: 'targetRootNoteId', - onDelete: RelationOnDeleteAction.SET_NULL, - relationType: RelationType.MANY_TO_ONE, - }, - }, // User-defined relation and field definitions { label: 'Post Card', @@ -485,249 +404,6 @@ export const EXPECTED_MANIFEST: Manifest = { relationType: RelationType.MANY_TO_ONE, }, }, - // Reverse relation fields for postCardRecipient (5 fields) - { - description: 'PostCardRecipient Post Card Recipient', - icon: 'IconTimelineEvent', - isNullable: true, - label: 'PostCardRecipient', - morphId: '20202020-9a2b-4c3d-a4e5-f6a7b8c9d0e1', - name: 'targetPostCardRecipient', - objectUniversalIdentifier: '20202020-6736-4337-b5c4-8b39fae325a5', - relationTargetFieldMetadataUniversalIdentifier: - '8f95b434-ee92-5c0f-95de-9d489af4d40c', - relationTargetObjectMetadataUniversalIdentifier: - 'e1a2b3c4-5e6f-4a7b-8c9d-0e1f2a3b4c5e', - type: FieldType.MORPH_RELATION, - universalIdentifier: 'a865f403-3843-5850-9a60-04e832220954', - universalSettings: { - joinColumnName: 'targetPostCardRecipientId', - onDelete: RelationOnDeleteAction.SET_NULL, - relationType: RelationType.MANY_TO_ONE, - }, - }, - { - description: 'PostCardRecipient Post Card Recipient', - icon: 'IconFileImport', - isNullable: true, - label: 'PostCardRecipient', - morphId: '20202020-f634-435d-ab8d-e1168b375c69', - name: 'targetPostCardRecipient', - objectUniversalIdentifier: '20202020-bd3d-4c60-8dca-571c71d4447a', - relationTargetFieldMetadataUniversalIdentifier: - '0c20df83-8cef-5cd6-9752-b98fb7780904', - relationTargetObjectMetadataUniversalIdentifier: - 'e1a2b3c4-5e6f-4a7b-8c9d-0e1f2a3b4c5e', - type: FieldType.MORPH_RELATION, - universalIdentifier: '501c46b7-7140-59a0-b703-9844bbcb34b9', - universalSettings: { - joinColumnName: 'targetPostCardRecipientId', - onDelete: RelationOnDeleteAction.SET_NULL, - relationType: RelationType.MANY_TO_ONE, - }, - }, - { - description: 'PostCardRecipient Post Card Recipient', - icon: 'IconCheckbox', - isNullable: true, - label: 'PostCardRecipient', - morphId: '20202020-f635-435d-ab8d-e1168b375c70', - name: 'targetPostCardRecipient', - objectUniversalIdentifier: '20202020-fff0-4b44-be82-bda313884400', - relationTargetFieldMetadataUniversalIdentifier: - 'b9642a90-2df5-555b-ab83-23cdce7a4142', - relationTargetObjectMetadataUniversalIdentifier: - 'e1a2b3c4-5e6f-4a7b-8c9d-0e1f2a3b4c5e', - type: FieldType.MORPH_RELATION, - universalIdentifier: '721418ff-1b53-58ef-8dd7-b58927c085c6', - universalSettings: { - joinColumnName: 'targetPostCardRecipientId', - onDelete: RelationOnDeleteAction.SET_NULL, - relationType: RelationType.MANY_TO_ONE, - }, - }, - { - description: 'PostCardRecipient Post Card Recipient', - icon: 'IconCheckbox', - isNullable: true, - label: 'PostCardRecipient', - morphId: '20202020-f636-435d-ab8d-e1168b375c71', - name: 'targetPostCardRecipient', - objectUniversalIdentifier: '20202020-5a9a-44e8-95df-771cd06d0fb1', - relationTargetFieldMetadataUniversalIdentifier: - 'd938a2d0-1279-5c48-b758-ac52f9460a76', - relationTargetObjectMetadataUniversalIdentifier: - 'e1a2b3c4-5e6f-4a7b-8c9d-0e1f2a3b4c5e', - type: FieldType.MORPH_RELATION, - universalIdentifier: 'feeba797-7ebb-5a36-91cd-7279ff92a2b6', - universalSettings: { - joinColumnName: 'targetPostCardRecipientId', - onDelete: RelationOnDeleteAction.SET_NULL, - relationType: RelationType.MANY_TO_ONE, - }, - }, - // Reverse relation fields for postCard (5 fields) - { - description: 'PostCard Post card', - icon: 'IconTimelineEvent', - isNullable: true, - label: 'PostCard', - morphId: '20202020-9a2b-4c3d-a4e5-f6a7b8c9d0e1', - name: 'targetPostCard', - objectUniversalIdentifier: '20202020-6736-4337-b5c4-8b39fae325a5', - relationTargetFieldMetadataUniversalIdentifier: - '9e58820c-78d7-539d-807c-d84362e1eab6', - relationTargetObjectMetadataUniversalIdentifier: - '54b589ca-eeed-4950-a176-358418b85c05', - type: FieldType.MORPH_RELATION, - universalIdentifier: '47d21bb5-075f-5ed4-ba89-c0e7322a57cd', - universalSettings: { - joinColumnName: 'targetPostCardId', - onDelete: RelationOnDeleteAction.SET_NULL, - relationType: RelationType.MANY_TO_ONE, - }, - }, - { - description: 'PostCard Post card', - icon: 'IconFileImport', - isNullable: true, - label: 'PostCard', - morphId: '20202020-f634-435d-ab8d-e1168b375c69', - name: 'targetPostCard', - objectUniversalIdentifier: '20202020-bd3d-4c60-8dca-571c71d4447a', - relationTargetFieldMetadataUniversalIdentifier: - '3f0d38c8-a50f-51c7-bdcb-84a715f4b92d', - relationTargetObjectMetadataUniversalIdentifier: - '54b589ca-eeed-4950-a176-358418b85c05', - type: FieldType.MORPH_RELATION, - universalIdentifier: '4d3cf1bd-d87d-5325-8fbc-19f72c252050', - universalSettings: { - joinColumnName: 'targetPostCardId', - onDelete: RelationOnDeleteAction.SET_NULL, - relationType: RelationType.MANY_TO_ONE, - }, - }, - { - description: 'PostCard Post card', - icon: 'IconCheckbox', - isNullable: true, - label: 'PostCard', - morphId: '20202020-f635-435d-ab8d-e1168b375c70', - name: 'targetPostCard', - objectUniversalIdentifier: '20202020-fff0-4b44-be82-bda313884400', - relationTargetFieldMetadataUniversalIdentifier: - 'def1f10a-847b-574e-a0a2-177caf02129c', - relationTargetObjectMetadataUniversalIdentifier: - '54b589ca-eeed-4950-a176-358418b85c05', - type: FieldType.MORPH_RELATION, - universalIdentifier: 'bc1f9abe-e27b-574a-9290-a1c9c8255a6b', - universalSettings: { - joinColumnName: 'targetPostCardId', - onDelete: RelationOnDeleteAction.SET_NULL, - relationType: RelationType.MANY_TO_ONE, - }, - }, - { - description: 'PostCard Post card', - icon: 'IconCheckbox', - isNullable: true, - label: 'PostCard', - morphId: '20202020-f636-435d-ab8d-e1168b375c71', - name: 'targetPostCard', - objectUniversalIdentifier: '20202020-5a9a-44e8-95df-771cd06d0fb1', - relationTargetFieldMetadataUniversalIdentifier: - 'b1238c2d-bc9c-53d6-8bee-eb045b9b9086', - relationTargetObjectMetadataUniversalIdentifier: - '54b589ca-eeed-4950-a176-358418b85c05', - type: FieldType.MORPH_RELATION, - universalIdentifier: 'fb610de1-db20-5e78-8e4a-7630e57148eb', - universalSettings: { - joinColumnName: 'targetPostCardId', - onDelete: RelationOnDeleteAction.SET_NULL, - relationType: RelationType.MANY_TO_ONE, - }, - }, - // Reverse relation fields for recipient (5 fields) - { - description: 'Recipient Recipient', - icon: 'IconTimelineEvent', - isNullable: true, - label: 'Recipient', - morphId: '20202020-9a2b-4c3d-a4e5-f6a7b8c9d0e1', - name: 'targetRecipient', - objectUniversalIdentifier: '20202020-6736-4337-b5c4-8b39fae325a5', - relationTargetFieldMetadataUniversalIdentifier: - '1118dd8a-9781-5254-b625-2d0f05eb46fe', - relationTargetObjectMetadataUniversalIdentifier: - 'd1a2b3c4-5e6f-4a7b-8c9d-0e1f2a3b4c5d', - type: FieldType.MORPH_RELATION, - universalIdentifier: '1ce06ca1-6e9a-53ce-9888-9152b1b2b628', - universalSettings: { - joinColumnName: 'targetRecipientId', - onDelete: RelationOnDeleteAction.SET_NULL, - relationType: RelationType.MANY_TO_ONE, - }, - }, - { - description: 'Recipient Recipient', - icon: 'IconFileImport', - isNullable: true, - label: 'Recipient', - morphId: '20202020-f634-435d-ab8d-e1168b375c69', - name: 'targetRecipient', - objectUniversalIdentifier: '20202020-bd3d-4c60-8dca-571c71d4447a', - relationTargetFieldMetadataUniversalIdentifier: - '15714c1a-d354-5407-8a15-25c117d605f8', - relationTargetObjectMetadataUniversalIdentifier: - 'd1a2b3c4-5e6f-4a7b-8c9d-0e1f2a3b4c5d', - type: FieldType.MORPH_RELATION, - universalIdentifier: '2081700d-7ce6-5698-a7da-a013feda0eb6', - universalSettings: { - joinColumnName: 'targetRecipientId', - onDelete: RelationOnDeleteAction.SET_NULL, - relationType: RelationType.MANY_TO_ONE, - }, - }, - { - description: 'Recipient Recipient', - icon: 'IconCheckbox', - isNullable: true, - label: 'Recipient', - morphId: '20202020-f635-435d-ab8d-e1168b375c70', - name: 'targetRecipient', - objectUniversalIdentifier: '20202020-fff0-4b44-be82-bda313884400', - relationTargetFieldMetadataUniversalIdentifier: - '167f6de7-835e-51e1-b751-34055eef6aa5', - relationTargetObjectMetadataUniversalIdentifier: - 'd1a2b3c4-5e6f-4a7b-8c9d-0e1f2a3b4c5d', - type: FieldType.MORPH_RELATION, - universalIdentifier: 'a93c479c-6206-5714-94ce-affa6e9886aa', - universalSettings: { - joinColumnName: 'targetRecipientId', - onDelete: RelationOnDeleteAction.SET_NULL, - relationType: RelationType.MANY_TO_ONE, - }, - }, - { - description: 'Recipient Recipient', - icon: 'IconCheckbox', - isNullable: true, - label: 'Recipient', - morphId: '20202020-f636-435d-ab8d-e1168b375c71', - name: 'targetRecipient', - objectUniversalIdentifier: '20202020-5a9a-44e8-95df-771cd06d0fb1', - relationTargetFieldMetadataUniversalIdentifier: - 'd0db1844-4fde-5a5a-b037-ebeabd4467af', - relationTargetObjectMetadataUniversalIdentifier: - 'd1a2b3c4-5e6f-4a7b-8c9d-0e1f2a3b4c5d', - type: FieldType.MORPH_RELATION, - universalIdentifier: '45752445-c4b6-5e80-8aa1-f5321a838b43', - universalSettings: { - joinColumnName: 'targetRecipientId', - onDelete: RelationOnDeleteAction.SET_NULL, - relationType: RelationType.MANY_TO_ONE, - }, - }, // Field on standard company object { defaultValue: false, @@ -766,70 +442,6 @@ export const EXPECTED_MANIFEST: Manifest = { type: FieldMetadataType.TEXT, universalIdentifier: '10452ca3-30cb-56fa-9e58-73f7b1c9fd65', }, - { - description: 'Root notes tied to the RootNote', - icon: 'IconBuildingSkyscraper', - isNullable: true, - label: 'Timeline Activities', - name: 'timelineActivities', - relationTargetFieldMetadataUniversalIdentifier: - '7f891268-9376-5d00-b8ec-bc32a023154d', - relationTargetObjectMetadataUniversalIdentifier: - '20202020-6736-4337-b5c4-8b39fae325a5', - type: FieldType.RELATION, - universalIdentifier: 'e1478d59-c947-5926-af8b-4b2f0f842c91', - universalSettings: { - relationType: RelationType.ONE_TO_MANY, - }, - }, - { - description: 'Root notes tied to the RootNote', - icon: 'IconBuildingSkyscraper', - isNullable: true, - label: 'Attachments', - name: 'attachments', - relationTargetFieldMetadataUniversalIdentifier: - 'd72bb002-9952-5651-983e-649436949113', - relationTargetObjectMetadataUniversalIdentifier: - '20202020-bd3d-4c60-8dca-571c71d4447a', - type: FieldType.RELATION, - universalIdentifier: '34a6b8e9-0187-57e1-86c1-41bacf1b2479', - universalSettings: { - relationType: RelationType.ONE_TO_MANY, - }, - }, - { - description: 'Root notes tied to the RootNote', - icon: 'IconBuildingSkyscraper', - isNullable: true, - label: 'Note Targets', - name: 'noteTargets', - relationTargetFieldMetadataUniversalIdentifier: - '61252ad6-2311-5054-ae51-b318dfd5fbd8', - relationTargetObjectMetadataUniversalIdentifier: - '20202020-fff0-4b44-be82-bda313884400', - type: FieldType.RELATION, - universalIdentifier: '7a62bbd6-e697-54b0-8c1c-163ac47c0305', - universalSettings: { - relationType: RelationType.ONE_TO_MANY, - }, - }, - { - description: 'Root notes tied to the RootNote', - icon: 'IconBuildingSkyscraper', - isNullable: true, - label: 'Task Targets', - name: 'taskTargets', - relationTargetFieldMetadataUniversalIdentifier: - '2f4f3d69-b78e-5465-9e87-17059959fca8', - relationTargetObjectMetadataUniversalIdentifier: - '20202020-5a9a-44e8-95df-771cd06d0fb1', - type: FieldType.RELATION, - universalIdentifier: '10c1af7e-e541-56bc-b5b3-d0297df3e23d', - universalSettings: { - relationType: RelationType.ONE_TO_MANY, - }, - }, ], icon: 'IconNote', labelIdentifierFieldMetadataUniversalIdentifier: @@ -862,70 +474,6 @@ export const EXPECTED_MANIFEST: Manifest = { type: FieldMetadataType.TEXT, universalIdentifier: 'f485cd3a-4c55-5b8f-b926-a83e5e6ba651', }, - { - description: 'Post Card Recipients tied to the PostCardRecipient', - icon: 'IconBuildingSkyscraper', - isNullable: true, - label: 'Timeline Activities', - name: 'timelineActivities', - relationTargetFieldMetadataUniversalIdentifier: - 'a865f403-3843-5850-9a60-04e832220954', - relationTargetObjectMetadataUniversalIdentifier: - '20202020-6736-4337-b5c4-8b39fae325a5', - type: FieldType.RELATION, - universalIdentifier: '8f95b434-ee92-5c0f-95de-9d489af4d40c', - universalSettings: { - relationType: RelationType.ONE_TO_MANY, - }, - }, - { - description: 'Post Card Recipients tied to the PostCardRecipient', - icon: 'IconBuildingSkyscraper', - isNullable: true, - label: 'Attachments', - name: 'attachments', - relationTargetFieldMetadataUniversalIdentifier: - '501c46b7-7140-59a0-b703-9844bbcb34b9', - relationTargetObjectMetadataUniversalIdentifier: - '20202020-bd3d-4c60-8dca-571c71d4447a', - type: FieldType.RELATION, - universalIdentifier: '0c20df83-8cef-5cd6-9752-b98fb7780904', - universalSettings: { - relationType: RelationType.ONE_TO_MANY, - }, - }, - { - description: 'Post Card Recipients tied to the PostCardRecipient', - icon: 'IconBuildingSkyscraper', - isNullable: true, - label: 'Note Targets', - name: 'noteTargets', - relationTargetFieldMetadataUniversalIdentifier: - '721418ff-1b53-58ef-8dd7-b58927c085c6', - relationTargetObjectMetadataUniversalIdentifier: - '20202020-fff0-4b44-be82-bda313884400', - type: FieldType.RELATION, - universalIdentifier: 'b9642a90-2df5-555b-ab83-23cdce7a4142', - universalSettings: { - relationType: RelationType.ONE_TO_MANY, - }, - }, - { - description: 'Post Card Recipients tied to the PostCardRecipient', - icon: 'IconBuildingSkyscraper', - isNullable: true, - label: 'Task Targets', - name: 'taskTargets', - relationTargetFieldMetadataUniversalIdentifier: - 'feeba797-7ebb-5a36-91cd-7279ff92a2b6', - relationTargetObjectMetadataUniversalIdentifier: - '20202020-5a9a-44e8-95df-771cd06d0fb1', - type: FieldType.RELATION, - universalIdentifier: 'd938a2d0-1279-5c48-b758-ac52f9460a76', - universalSettings: { - relationType: RelationType.ONE_TO_MANY, - }, - }, ], icon: 'IconLink', labelIdentifierFieldMetadataUniversalIdentifier: @@ -1025,70 +573,6 @@ export const EXPECTED_MANIFEST: Manifest = { type: FieldMetadataType.TEXT, universalIdentifier: '3e14e30f-8271-5a13-9c1f-ce5edb932325', }, - { - description: 'Post cards tied to the PostCard', - icon: 'IconBuildingSkyscraper', - isNullable: true, - label: 'Timeline Activities', - name: 'timelineActivities', - relationTargetFieldMetadataUniversalIdentifier: - '47d21bb5-075f-5ed4-ba89-c0e7322a57cd', - relationTargetObjectMetadataUniversalIdentifier: - '20202020-6736-4337-b5c4-8b39fae325a5', - type: FieldType.RELATION, - universalIdentifier: '9e58820c-78d7-539d-807c-d84362e1eab6', - universalSettings: { - relationType: RelationType.ONE_TO_MANY, - }, - }, - { - description: 'Post cards tied to the PostCard', - icon: 'IconBuildingSkyscraper', - isNullable: true, - label: 'Attachments', - name: 'attachments', - relationTargetFieldMetadataUniversalIdentifier: - '4d3cf1bd-d87d-5325-8fbc-19f72c252050', - relationTargetObjectMetadataUniversalIdentifier: - '20202020-bd3d-4c60-8dca-571c71d4447a', - type: FieldType.RELATION, - universalIdentifier: '3f0d38c8-a50f-51c7-bdcb-84a715f4b92d', - universalSettings: { - relationType: RelationType.ONE_TO_MANY, - }, - }, - { - description: 'Post cards tied to the PostCard', - icon: 'IconBuildingSkyscraper', - isNullable: true, - label: 'Note Targets', - name: 'noteTargets', - relationTargetFieldMetadataUniversalIdentifier: - 'bc1f9abe-e27b-574a-9290-a1c9c8255a6b', - relationTargetObjectMetadataUniversalIdentifier: - '20202020-fff0-4b44-be82-bda313884400', - type: FieldType.RELATION, - universalIdentifier: 'def1f10a-847b-574e-a0a2-177caf02129c', - universalSettings: { - relationType: RelationType.ONE_TO_MANY, - }, - }, - { - description: 'Post cards tied to the PostCard', - icon: 'IconBuildingSkyscraper', - isNullable: true, - label: 'Task Targets', - name: 'taskTargets', - relationTargetFieldMetadataUniversalIdentifier: - 'fb610de1-db20-5e78-8e4a-7630e57148eb', - relationTargetObjectMetadataUniversalIdentifier: - '20202020-5a9a-44e8-95df-771cd06d0fb1', - type: FieldType.RELATION, - universalIdentifier: 'b1238c2d-bc9c-53d6-8bee-eb045b9b9086', - universalSettings: { - relationType: RelationType.ONE_TO_MANY, - }, - }, ], icon: 'IconMail', labelIdentifierFieldMetadataUniversalIdentifier: @@ -1126,70 +610,6 @@ export const EXPECTED_MANIFEST: Manifest = { type: FieldMetadataType.TEXT, universalIdentifier: 'c400ccb8-cd91-5ae1-81e3-2df8b6f2cf53', }, - { - description: 'Recipients tied to the Recipient', - icon: 'IconBuildingSkyscraper', - isNullable: true, - label: 'Timeline Activities', - name: 'timelineActivities', - relationTargetFieldMetadataUniversalIdentifier: - '1ce06ca1-6e9a-53ce-9888-9152b1b2b628', - relationTargetObjectMetadataUniversalIdentifier: - '20202020-6736-4337-b5c4-8b39fae325a5', - type: FieldType.RELATION, - universalIdentifier: '1118dd8a-9781-5254-b625-2d0f05eb46fe', - universalSettings: { - relationType: RelationType.ONE_TO_MANY, - }, - }, - { - description: 'Recipients tied to the Recipient', - icon: 'IconBuildingSkyscraper', - isNullable: true, - label: 'Attachments', - name: 'attachments', - relationTargetFieldMetadataUniversalIdentifier: - '2081700d-7ce6-5698-a7da-a013feda0eb6', - relationTargetObjectMetadataUniversalIdentifier: - '20202020-bd3d-4c60-8dca-571c71d4447a', - type: FieldType.RELATION, - universalIdentifier: '15714c1a-d354-5407-8a15-25c117d605f8', - universalSettings: { - relationType: RelationType.ONE_TO_MANY, - }, - }, - { - description: 'Recipients tied to the Recipient', - icon: 'IconBuildingSkyscraper', - isNullable: true, - label: 'Note Targets', - name: 'noteTargets', - relationTargetFieldMetadataUniversalIdentifier: - 'a93c479c-6206-5714-94ce-affa6e9886aa', - relationTargetObjectMetadataUniversalIdentifier: - '20202020-fff0-4b44-be82-bda313884400', - type: FieldType.RELATION, - universalIdentifier: '167f6de7-835e-51e1-b751-34055eef6aa5', - universalSettings: { - relationType: RelationType.ONE_TO_MANY, - }, - }, - { - description: 'Recipients tied to the Recipient', - icon: 'IconBuildingSkyscraper', - isNullable: true, - label: 'Task Targets', - name: 'taskTargets', - relationTargetFieldMetadataUniversalIdentifier: - '45752445-c4b6-5e80-8aa1-f5321a838b43', - relationTargetObjectMetadataUniversalIdentifier: - '20202020-5a9a-44e8-95df-771cd06d0fb1', - type: FieldType.RELATION, - universalIdentifier: 'd0db1844-4fde-5a5a-b037-ebeabd4467af', - universalSettings: { - relationType: RelationType.ONE_TO_MANY, - }, - }, ], icon: 'IconUser', labelIdentifierFieldMetadataUniversalIdentifier: diff --git a/packages/twenty-sdk/src/cli/__tests__/apps/rich-app/__integration__/app-dev/tests/manifest.tests.ts b/packages/twenty-sdk/src/cli/__tests__/apps/rich-app/__integration__/app-dev/tests/manifest.tests.ts index e928b6ce5d..d182ae51b9 100644 --- a/packages/twenty-sdk/src/cli/__tests__/apps/rich-app/__integration__/app-dev/tests/manifest.tests.ts +++ b/packages/twenty-sdk/src/cli/__tests__/apps/rich-app/__integration__/app-dev/tests/manifest.tests.ts @@ -23,7 +23,7 @@ export const defineManifestTests = (appPath: string): void => { expect(manifest.logicFunctions).toHaveLength(7); expect(manifest.frontComponents).toHaveLength(4); expect(manifest.roles).toHaveLength(2); - expect(manifest.fields).toHaveLength(23); + expect(manifest.fields).toHaveLength(7); expect(manifest.views).toHaveLength(5); expect(manifest.navigationMenuItems).toHaveLength(3); expect(manifest.pageLayoutTabs).toHaveLength(1); 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 954cca8a18..9dabaadee4 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 @@ -38,6 +38,7 @@ exports[`stub-twenty-sdk-define plugin > matches the recorded export partition 1 "favoriteRecordIds", "featureFlags", "getFieldUniversalIdentifier", + "getSystemRelationFieldUniversalIdentifier", "hasAnySoftDeleteFilterOnView", "includes", "includesEvery", diff --git a/packages/twenty-sdk/src/cli/utilities/build/manifest/manifest-build.ts b/packages/twenty-sdk/src/cli/utilities/build/manifest/manifest-build.ts index ebda0130cd..d69c84532e 100644 --- a/packages/twenty-sdk/src/cli/utilities/build/manifest/manifest-build.ts +++ b/packages/twenty-sdk/src/cli/utilities/build/manifest/manifest-build.ts @@ -502,13 +502,11 @@ export const buildManifest = async ( if (applicationConfig) { for (const objectConfig of objectConfigs) { - const { - objectFields: objectFieldsWithDefaults, - fields: reverseRelationFields, - } = getDefaultFieldsInObjectFields({ - objectConfig, - applicationUniversalIdentifier: applicationConfig.universalIdentifier, - }); + const { objectFields: objectFieldsWithDefaults } = + getDefaultFieldsInObjectFields({ + objectConfig, + applicationUniversalIdentifier: applicationConfig.universalIdentifier, + }); const labelIdentifierFieldMetadataUniversalIdentifier = objectConfig.labelIdentifierFieldMetadataUniversalIdentifier ?? @@ -529,7 +527,6 @@ export const buildManifest = async ( }; objects.push(objectManifest); - fields.push(...reverseRelationFields); } } diff --git a/packages/twenty-sdk/src/cli/utilities/build/manifest/utils/get-default-fields-in-object-fields.ts b/packages/twenty-sdk/src/cli/utilities/build/manifest/utils/get-default-fields-in-object-fields.ts index ae7477a290..22136568f2 100644 --- a/packages/twenty-sdk/src/cli/utilities/build/manifest/utils/get-default-fields-in-object-fields.ts +++ b/packages/twenty-sdk/src/cli/utilities/build/manifest/utils/get-default-fields-in-object-fields.ts @@ -1,7 +1,5 @@ -import { getDefaultRelationObjectFields } from '@/cli/utilities/build/manifest/utils/get-default-relation-object-fields'; import type { ObjectConfig } from '@/sdk/define/objects/object-config'; import { - type FieldManifest, getFieldUniversalIdentifier, type ObjectFieldManifest, } from 'twenty-shared/application'; @@ -34,13 +32,7 @@ export const getDefaultFieldsInObjectFields = ({ }: { objectConfig: ObjectConfig; applicationUniversalIdentifier: string; -}): { objectFields: ObjectFieldManifest[]; fields: FieldManifest[] } => { - const { objectFields: defaultRelationObjectFields, fields: reverseFields } = - getDefaultRelationObjectFields({ - objectConfig, - applicationUniversalIdentifier, - }); - +}): { objectFields: ObjectFieldManifest[] } => { const objectConfigFieldNames = (objectConfig.fields ?? []).map( (field) => field.name, ); @@ -56,11 +48,5 @@ export const getDefaultFieldsInObjectFields = ({ objectFieldsWithDefaults.push(defaultNameObjectField); } - for (const defaultRelationField of defaultRelationObjectFields) { - if (!objectConfigFieldNames.includes(defaultRelationField.name)) { - objectFieldsWithDefaults.push(defaultRelationField); - } - } - - return { objectFields: objectFieldsWithDefaults, fields: reverseFields }; + return { objectFields: objectFieldsWithDefaults }; }; diff --git a/packages/twenty-sdk/src/cli/utilities/build/manifest/utils/get-default-relation-object-fields.ts b/packages/twenty-sdk/src/cli/utilities/build/manifest/utils/get-default-relation-object-fields.ts deleted file mode 100644 index 8f5a203032..0000000000 --- a/packages/twenty-sdk/src/cli/utilities/build/manifest/utils/get-default-relation-object-fields.ts +++ /dev/null @@ -1,174 +0,0 @@ -import { RelationType } from '@/sdk/define'; -import type { ObjectConfig } from '@/sdk/define/objects/object-config'; -import { - type FieldManifest, - getFieldUniversalIdentifier, - type ObjectFieldManifest, -} from 'twenty-shared/application'; -import { STANDARD_OBJECTS } from 'twenty-shared/metadata'; -import { FieldMetadataType, RelationOnDeleteAction } from 'twenty-shared/types'; -import { capitalize } from 'twenty-shared/utils'; - -type DefaultRelationConfig = { - fieldName: string; - label: string; - targetFieldName: (objectConfig: ObjectConfig) => string; - targetLabel: (objectConfig: ObjectConfig) => string; - icon: string; - targetIcon: string; - standardObjectKey: keyof typeof STANDARD_OBJECTS; -} & ( - | { targetFieldType: FieldMetadataType.RELATION } - | { targetFieldType: FieldMetadataType.MORPH_RELATION; morphId: string } -); - -const DEFAULT_DEFAULT_RELATION = { - targetFieldName: (objectConfig: ObjectConfig) => - `target${capitalize(objectConfig.nameSingular)}`, - targetLabel: (objectConfig: ObjectConfig) => - capitalize(objectConfig.nameSingular), - icon: 'IconBuildingSkyscraper', - isNullable: true, -}; - -const DEFAULT_RELATION_CONFIGS = [ - { - ...DEFAULT_DEFAULT_RELATION, - fieldName: 'timelineActivities', - label: 'Timeline Activities', - targetIcon: 'IconTimelineEvent', - targetFieldType: FieldMetadataType.MORPH_RELATION, - standardObjectKey: 'timelineActivity', - morphId: STANDARD_OBJECTS.timelineActivity.morphIds.targetMorphId.morphId, - }, - { - ...DEFAULT_DEFAULT_RELATION, - fieldName: 'attachments', - label: 'Attachments', - icon: 'IconBuildingSkyscraper', - targetIcon: 'IconFileImport', - targetFieldType: FieldMetadataType.MORPH_RELATION, - standardObjectKey: 'attachment', - morphId: STANDARD_OBJECTS.attachment.morphIds.targetMorphId.morphId, - }, - { - ...DEFAULT_DEFAULT_RELATION, - fieldName: 'noteTargets', - label: 'Note Targets', - icon: 'IconBuildingSkyscraper', - targetIcon: 'IconCheckbox', - targetFieldType: FieldMetadataType.MORPH_RELATION, - standardObjectKey: 'noteTarget', - morphId: STANDARD_OBJECTS.noteTarget.morphIds.targetMorphId.morphId, - }, - { - ...DEFAULT_DEFAULT_RELATION, - fieldName: 'taskTargets', - label: 'Task Targets', - icon: 'IconBuildingSkyscraper', - targetIcon: 'IconCheckbox', - targetFieldType: FieldMetadataType.MORPH_RELATION, - standardObjectKey: 'taskTarget', - morphId: STANDARD_OBJECTS.taskTarget.morphIds.targetMorphId.morphId, - }, -] as const satisfies DefaultRelationConfig[]; - -const buildReverseField = ({ - config, - forwardFieldUniversalIdentifier, - objectConfig, - universalIdentifier, -}: { - config: DefaultRelationConfig; - objectConfig: ObjectConfig; - universalIdentifier: string; - forwardFieldUniversalIdentifier: string; -}): FieldManifest => { - const standardObject = STANDARD_OBJECTS[config.standardObjectKey]; - - const commonFields = { - name: config.targetFieldName(objectConfig), - label: config.targetLabel(objectConfig), - description: `${config.targetLabel(objectConfig)} ${objectConfig.labelSingular}`, - icon: config.targetIcon, - isNullable: true, - universalSettings: { - relationType: RelationType.MANY_TO_ONE, - onDelete: RelationOnDeleteAction.SET_NULL, - joinColumnName: `target${capitalize(objectConfig.nameSingular)}Id`, - }, - universalIdentifier, - objectUniversalIdentifier: standardObject.universalIdentifier, - relationTargetFieldMetadataUniversalIdentifier: - forwardFieldUniversalIdentifier, - relationTargetObjectMetadataUniversalIdentifier: - objectConfig.universalIdentifier, - }; - - if (config.targetFieldType === FieldMetadataType.MORPH_RELATION) { - return { - ...commonFields, - type: config.targetFieldType, - morphId: config.morphId, - }; - } - - return { - ...commonFields, - type: config.targetFieldType, - }; -}; - -export const getDefaultRelationObjectFields = ({ - objectConfig, - applicationUniversalIdentifier, -}: { - objectConfig: ObjectConfig; - applicationUniversalIdentifier: string; -}): { objectFields: ObjectFieldManifest[]; fields: FieldManifest[] } => { - const objectFields: ObjectFieldManifest[] = []; - const fields: FieldManifest[] = []; - - for (const config of DEFAULT_RELATION_CONFIGS) { - const standardObject = STANDARD_OBJECTS[config.standardObjectKey]; - - const forwardFieldUniversalIdentifier = getFieldUniversalIdentifier({ - applicationUniversalIdentifier, - objectUniversalIdentifier: objectConfig.universalIdentifier, - name: config.fieldName, - }); - - const reverseFieldUniversalIdentifier = getFieldUniversalIdentifier({ - applicationUniversalIdentifier, - objectUniversalIdentifier: standardObject.universalIdentifier, - name: config.targetFieldName(objectConfig), - }); - - const forwardField: ObjectFieldManifest = { - name: config.fieldName, - label: config.label, - description: `${objectConfig.labelPlural} tied to the ${config.targetLabel(objectConfig)}`, - icon: config.icon, - isNullable: config.isNullable, - type: FieldMetadataType.RELATION, - universalSettings: { relationType: RelationType.ONE_TO_MANY }, - universalIdentifier: forwardFieldUniversalIdentifier, - relationTargetFieldMetadataUniversalIdentifier: - reverseFieldUniversalIdentifier, - relationTargetObjectMetadataUniversalIdentifier: - standardObject.universalIdentifier, - }; - - const reverseField = buildReverseField({ - config, - objectConfig, - universalIdentifier: reverseFieldUniversalIdentifier, - forwardFieldUniversalIdentifier, - }); - - objectFields.push(forwardField); - fields.push(reverseField); - } - - return { objectFields, fields }; -}; diff --git a/packages/twenty-sdk/src/sdk/define/index.ts b/packages/twenty-sdk/src/sdk/define/index.ts index 44320b7839..55bf7f912c 100644 --- a/packages/twenty-sdk/src/sdk/define/index.ts +++ b/packages/twenty-sdk/src/sdk/define/index.ts @@ -23,7 +23,10 @@ export type { } from '@/sdk/define/fields/composite-fields'; export { defineField } from '@/sdk/define/fields/define-field'; export { FieldType } from '@/sdk/define/fields/field-type'; -export { getFieldUniversalIdentifier } from 'twenty-shared/application'; +export { + getFieldUniversalIdentifier, + getSystemRelationFieldUniversalIdentifier, +} from 'twenty-shared/application'; export { OnDeleteAction } from '@/sdk/define/fields/on-delete-action'; export { RelationType } from '@/sdk/define/fields/relation-type'; export { validateFields } from '@/sdk/define/fields/validate-fields'; diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-21/1-21-workspace-command-1775500004000-backfill-message-thread-subject.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-21/1-21-workspace-command-1775500004000-backfill-message-thread-subject.command.ts index 995e0f304b..de3c451fa1 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-21/1-21-workspace-command-1775500004000-backfill-message-thread-subject.command.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/1-21/1-21-workspace-command-1775500004000-backfill-message-thread-subject.command.ts @@ -221,7 +221,8 @@ export class BackfillMessageThreadSubjectCommand extends ProvisionedWorkspaceCom universalIdentifier: STANDARD_OBJECTS.messageThread.fields.subject.universalIdentifier, }, - flatApplication: twentyStandardFlatApplication, + applicationUniversalIdentifier: + twentyStandardFlatApplication.universalIdentifier, objectMetadataUniversalIdentifier: messageThreadObjectMetadata.universalIdentifier, }), diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/2-23/2-23-upgrade-version-command.module.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/2-23/2-23-upgrade-version-command.module.ts index 21510f74b3..2877a87d39 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/2-23/2-23-upgrade-version-command.module.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/2-23/2-23-upgrade-version-command.module.ts @@ -1,22 +1,34 @@ import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; import { WorkspaceIteratorModule } from 'src/database/commands/command-runners/workspace-iterator.module'; import { AddWorkflowCoreSoftRefFieldCommand } from 'src/database/commands/upgrade-version-command/2-23/2-23-workspace-command-1784286706000-add-workflow-core-soft-ref-field.command'; import { BackfillWorkflowCoreLinksCommand } from 'src/database/commands/upgrade-version-command/2-23/2-23-workspace-command-1784286707000-backfill-workflow-core-links.command'; +import { ReconcileSystemRelationFieldUniversalIdentifierCommand } from 'src/database/commands/upgrade-version-command/2-23/2-23-workspace-command-1784565136000-reconcile-system-relation-field-universal-identifier.command'; +import { UpgradePeopleDataLabsApplicationCommand } from 'src/database/commands/upgrade-version-command/2-23/2-23-workspace-command-1784565137000-upgrade-people-data-labs-application.command'; +import { ApplicationUpgradeModule } from 'src/engine/core-modules/application/application-upgrade/application-upgrade.module'; +import { ApplicationEntity } from 'src/engine/core-modules/application/application.entity'; import { ApplicationModule } from 'src/engine/core-modules/application/application.module'; +import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache.module'; +import { WorkspaceMigrationRunnerModule } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/workspace-migration-runner.module'; import { WorkspaceMigrationModule } from 'src/engine/workspace-manager/workspace-migration/workspace-migration.module'; @Module({ imports: [ + TypeOrmModule.forFeature([FieldMetadataEntity, ApplicationEntity]), ApplicationModule, WorkspaceCacheModule, WorkspaceMigrationModule, + WorkspaceMigrationRunnerModule, WorkspaceIteratorModule, + ApplicationUpgradeModule, ], providers: [ AddWorkflowCoreSoftRefFieldCommand, BackfillWorkflowCoreLinksCommand, + ReconcileSystemRelationFieldUniversalIdentifierCommand, + UpgradePeopleDataLabsApplicationCommand, ], }) export class V2_23_UpgradeVersionCommandModule {} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/2-23/2-23-workspace-command-1784565136000-reconcile-system-relation-field-universal-identifier.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/2-23/2-23-workspace-command-1784565136000-reconcile-system-relation-field-universal-identifier.command.ts new file mode 100644 index 0000000000..4c54e2fcb6 --- /dev/null +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/2-23/2-23-workspace-command-1784565136000-reconcile-system-relation-field-universal-identifier.command.ts @@ -0,0 +1,276 @@ +import { InjectRepository } from '@nestjs/typeorm'; + +import { Command } from 'nest-commander'; +import { getSystemRelationFieldUniversalIdentifier } from 'twenty-shared/application'; +import { + DEFAULT_RELATIONS_OBJECTS_STANDARD_IDS, + STANDARD_OBJECTS, +} from 'twenty-shared/metadata'; +import { FieldMetadataType } from 'twenty-shared/types'; +import { capitalize, isDefined } from 'twenty-shared/utils'; +import { Repository } from 'typeorm'; + +import { ProvisionedWorkspaceCommandRunner } from 'src/database/commands/command-runners/provisioned-workspace.command-runner'; +import { WorkspaceIteratorService } from 'src/database/commands/command-runners/workspace-iterator.service'; +import { type RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspace.command-runner'; +import { RegisteredWorkspaceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-workspace-command.decorator'; +import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; +import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util'; +import { getMetadataFlatEntityMapsKey } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-flat-entity-maps-key.util'; +import { getMetadataRelatedMetadataNames } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-related-metadata-names.util'; +import { getMetadataSerializedRelationNames } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-serialized-relation-names.util'; +import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service'; +import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-migration/constant/standard-object-icons'; +import { WorkspaceMigrationRunnerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/services/workspace-migration-runner.service'; + +const REVERSE_SYSTEM_RELATION_FIELD_NAME_PREFIX = 'target'; + +// Every provisioning path of the default relations (legacy SDK manifest, legacy +// API transpiler, twenty-standard) stamps the reverse morph field with the +// engine-defined targetMorphId of its host standard object, while user-created +// morph relations carry freshly generated morphIds. Matching on this morphId +// (in addition to the name prefix) makes the reconciliation an exact +// fingerprint and cannot hijack a user-authored `target*` morph field hosted +// on one of the four standard relation objects. +const TARGET_MORPH_ID_BY_DEFAULT_RELATION_OBJECT_UNIVERSAL_IDENTIFIER: Record< + string, + string +> = Object.fromEntries( + DEFAULT_RELATIONS_OBJECTS_STANDARD_IDS.map((standardObjectNameSingular) => [ + STANDARD_OBJECTS[standardObjectNameSingular].universalIdentifier, + STANDARD_OBJECTS[standardObjectNameSingular].morphIds.targetMorphId.morphId, + ]), +); + +type SystemRelationFieldUpdate = { + id: string; + update: Partial< + Pick< + FieldMetadataEntity, + 'universalIdentifier' | 'isSystemSideEffect' | 'label' | 'icon' + > + >; +}; + +@RegisteredWorkspaceCommand('2.23.0', 1784565136000) +@Command({ + name: 'upgrade:2-23:reconcile-system-relation-field-universal-identifier', + description: + 'Reconcile the default relations (timelineActivity/attachment/noteTarget/taskTarget) with the engine convention, for standard and custom source objects alike. Both sides get the name-free deterministic universal identifier (host and relation target object identifiers, direction encoded by swapping them) and isSystemSideEffect: true; reverse morph fields additionally get the name-derived label/icon. An object rename becomes a lossless update and standard fields match custom ones, as if twenty-standard objects had been provisioned by the side-effect engine.', +}) +export class ReconcileSystemRelationFieldUniversalIdentifierCommand extends ProvisionedWorkspaceCommandRunner { + constructor( + protected readonly workspaceIteratorService: WorkspaceIteratorService, + private readonly workspaceCacheService: WorkspaceCacheService, + private readonly workspaceMigrationRunnerService: WorkspaceMigrationRunnerService, + @InjectRepository(FieldMetadataEntity) + private readonly fieldMetadataRepository: Repository, + ) { + super(workspaceIteratorService); + } + + override async runOnWorkspace({ + workspaceId, + options, + }: RunOnWorkspaceArgs): Promise { + const isDryRun = options.dryRun ?? false; + + const { flatFieldMetadataMaps, flatObjectMetadataMaps } = + await this.workspaceCacheService.getOrRecompute(workspaceId, [ + 'flatFieldMetadataMaps', + 'flatObjectMetadataMaps', + ]); + + const systemRelationFieldUpdates: SystemRelationFieldUpdate[] = []; + + for (const flatFieldMetadata of Object.values( + flatFieldMetadataMaps.byUniversalIdentifier, + )) { + if ( + !isDefined(flatFieldMetadata) || + flatFieldMetadata.type !== FieldMetadataType.MORPH_RELATION || + !flatFieldMetadata.name.startsWith( + REVERSE_SYSTEM_RELATION_FIELD_NAME_PREFIX, + ) || + !isDefined(flatFieldMetadata.relationTargetObjectMetadataId) + ) { + continue; + } + + const hostFlatObjectMetadata = findFlatEntityByIdInFlatEntityMaps({ + flatEntityMaps: flatObjectMetadataMaps, + flatEntityId: flatFieldMetadata.objectMetadataId, + }); + + if (!isDefined(hostFlatObjectMetadata)) { + continue; + } + + const expectedTargetMorphId = + TARGET_MORPH_ID_BY_DEFAULT_RELATION_OBJECT_UNIVERSAL_IDENTIFIER[ + hostFlatObjectMetadata.universalIdentifier + ]; + + if ( + !isDefined(expectedTargetMorphId) || + flatFieldMetadata.morphId !== expectedTargetMorphId + ) { + continue; + } + + const sourceFlatObjectMetadata = findFlatEntityByIdInFlatEntityMaps({ + flatEntityMaps: flatObjectMetadataMaps, + flatEntityId: flatFieldMetadata.relationTargetObjectMetadataId, + }); + + if (!isDefined(sourceFlatObjectMetadata)) { + this.logger.warn( + `Missing source object for reverse relation field ${flatFieldMetadata.name} (${flatFieldMetadata.id}) in workspace ${workspaceId}, skipping`, + ); + continue; + } + + const derivedUniversalIdentifier = + getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + sourceFlatObjectMetadata.applicationUniversalIdentifier, + objectUniversalIdentifier: hostFlatObjectMetadata.universalIdentifier, + relationTargetObjectUniversalIdentifier: + sourceFlatObjectMetadata.universalIdentifier, + }); + // Name-derived label + host-object icon, matching the engine provisioner + // convention already used for custom objects. + const derivedLabel = capitalize(sourceFlatObjectMetadata.nameSingular); + const derivedIcon = + STANDARD_OBJECT_ICONS[ + hostFlatObjectMetadata.nameSingular as keyof typeof STANDARD_OBJECT_ICONS + ] ?? 'IconBuildingSkyscraper'; + + const update: SystemRelationFieldUpdate['update'] = {}; + + if ( + flatFieldMetadata.universalIdentifier !== derivedUniversalIdentifier + ) { + update.universalIdentifier = derivedUniversalIdentifier; + } + if (!flatFieldMetadata.isSystemSideEffect) { + update.isSystemSideEffect = true; + } + if (flatFieldMetadata.label !== derivedLabel) { + update.label = derivedLabel; + } + if (flatFieldMetadata.icon !== derivedIcon) { + update.icon = derivedIcon; + } + + if (Object.keys(update).length > 0) { + systemRelationFieldUpdates.push({ id: flatFieldMetadata.id, update }); + } + + // Both sides of a default relation are engine-owned, for standard and + // custom source objects alike: the forward field on the source object + // was provisioned alongside the reverse field (by the side-effect engine + // for custom objects, by twenty-standard for standard ones), so it must + // carry isSystemSideEffect and the same name-free deterministic + // universal identifier as the reverse side, with host and relation + // target swapped — matching what the create side-effect handler emits + // for objects created post-2.23. + const forwardFlatFieldMetadata = isDefined( + flatFieldMetadata.relationTargetFieldMetadataId, + ) + ? findFlatEntityByIdInFlatEntityMaps({ + flatEntityMaps: flatFieldMetadataMaps, + flatEntityId: flatFieldMetadata.relationTargetFieldMetadataId, + }) + : undefined; + + if (!isDefined(forwardFlatFieldMetadata)) { + this.logger.warn( + `Missing forward field for reverse relation field ${flatFieldMetadata.name} (${flatFieldMetadata.id}) in workspace ${workspaceId}, skipping`, + ); + continue; + } + + const derivedForwardUniversalIdentifier = + getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + sourceFlatObjectMetadata.applicationUniversalIdentifier, + objectUniversalIdentifier: + sourceFlatObjectMetadata.universalIdentifier, + relationTargetObjectUniversalIdentifier: + hostFlatObjectMetadata.universalIdentifier, + }); + + const forwardUpdate: SystemRelationFieldUpdate['update'] = {}; + + if ( + forwardFlatFieldMetadata.universalIdentifier !== + derivedForwardUniversalIdentifier + ) { + forwardUpdate.universalIdentifier = derivedForwardUniversalIdentifier; + } + if (!forwardFlatFieldMetadata.isSystemSideEffect) { + forwardUpdate.isSystemSideEffect = true; + } + + if (Object.keys(forwardUpdate).length > 0) { + systemRelationFieldUpdates.push({ + id: forwardFlatFieldMetadata.id, + update: forwardUpdate, + }); + } + } + + if (systemRelationFieldUpdates.length === 0) { + this.logger.log( + `No default-relation field to reconcile for workspace ${workspaceId}`, + ); + + return; + } + + this.logger.log( + `${isDryRun ? '[DRY RUN] ' : ''}Reconciling ${systemRelationFieldUpdates.length} default-relation field(s) for workspace ${workspaceId}`, + ); + + if (isDryRun) { + return; + } + + // Single transaction per workspace: a partial backfill would leave old and + // new universal identifiers coexisting, breaking the reconcile invariant + // for the follow-up people-data-labs upgrade command. + await this.fieldMetadataRepository.manager.transaction( + async (entityManager) => { + const transactionalFieldMetadataRepository = + entityManager.getRepository(FieldMetadataEntity); + + for (const { id, update } of systemRelationFieldUpdates) { + await transactionalFieldMetadataRepository.update( + { id, workspaceId }, + update, + ); + } + }, + ); + + const fieldMetadataRelatedNames = [ + 'fieldMetadata', + ...getMetadataRelatedMetadataNames('fieldMetadata'), + ...getMetadataSerializedRelationNames('fieldMetadata'), + 'index', + ] as const; + const allFlatEntityMapsKeys = [ + ...new Set(fieldMetadataRelatedNames.map(getMetadataFlatEntityMapsKey)), + ]; + + await this.workspaceMigrationRunnerService.invalidateCache({ + allFlatEntityMapsKeys, + workspaceId, + }); + + this.logger.log( + `Reconciled ${systemRelationFieldUpdates.length} default-relation field(s) for workspace ${workspaceId}`, + ); + } +} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/2-23/2-23-workspace-command-1784565137000-upgrade-people-data-labs-application.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/2-23/2-23-workspace-command-1784565137000-upgrade-people-data-labs-application.command.ts new file mode 100644 index 0000000000..daf25e6d0c --- /dev/null +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/2-23/2-23-workspace-command-1784565137000-upgrade-people-data-labs-application.command.ts @@ -0,0 +1,112 @@ +import { InjectRepository } from '@nestjs/typeorm'; + +import { Command } from 'nest-commander'; +import * as semver from 'semver'; +import { isDefined } from 'twenty-shared/utils'; +import { Repository } from 'typeorm'; + +import { ProvisionedWorkspaceCommandRunner } from 'src/database/commands/command-runners/provisioned-workspace.command-runner'; +import { WorkspaceIteratorService } from 'src/database/commands/command-runners/workspace-iterator.service'; +import { type RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspace.command-runner'; +import { ApplicationUpgradeService } from 'src/engine/core-modules/application/application-upgrade/application-upgrade.service'; +import { ApplicationEntity } from 'src/engine/core-modules/application/application.entity'; +import { RegisteredWorkspaceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-workspace-command.decorator'; + +// packages/twenty-apps/public/people-data-labs APPLICATION_UNIVERSAL_IDENTIFIER +const PEOPLE_DATA_LABS_APPLICATION_UNIVERSAL_IDENTIFIER = + '4a1178c1-3535-4a47-b592-231d3216b36f'; + +// First version whose views pin the re-derived name-free system relation +// field universal identifiers, published ahead of the 2.23 release. +const PEOPLE_DATA_LABS_TARGET_VERSION = '1.0.7'; + +// The people-data-labs views reference the person/company forward system +// relation fields (noteTargets/taskTargets/attachments/timelineActivities) by +// universal identifier. The 2.23 reconcile command re-derives those +// identifiers name-free, so installed versions <= 1.0.6 hold dangling +// references and any re-sync of the stored manifest would fail. Upgrading the +// app right after the backfill closes that desync window. The command pins +// the target version rather than resolving latest, so the upgrade is +// deterministic and idempotent; workspaces already at or above it are left +// untouched. +@RegisteredWorkspaceCommand('2.23.0', 1784565137000) +@Command({ + name: 'upgrade:2-23:upgrade-people-data-labs-application', + description: + 'Upgrade the people-data-labs application to 1.0.7 right after the system relation field universal identifier backfill, so its views reference the re-derived name-free identifiers instead of the stale pre-2.23 ones. Workspaces already at or above 1.0.7 are left untouched.', +}) +export class UpgradePeopleDataLabsApplicationCommand extends ProvisionedWorkspaceCommandRunner { + constructor( + protected readonly workspaceIteratorService: WorkspaceIteratorService, + private readonly applicationUpgradeService: ApplicationUpgradeService, + @InjectRepository(ApplicationEntity) + private readonly applicationRepository: Repository, + ) { + super(workspaceIteratorService); + } + + override async runOnWorkspace({ + workspaceId, + options, + }: RunOnWorkspaceArgs): Promise { + const isDryRun = options.dryRun ?? false; + + const application = await this.applicationRepository.findOne({ + where: { + workspaceId, + universalIdentifier: PEOPLE_DATA_LABS_APPLICATION_UNIVERSAL_IDENTIFIER, + }, + relations: ['applicationRegistration'], + }); + + if (!isDefined(application)) { + return; + } + + const applicationRegistration = application.applicationRegistration; + + if (!isDefined(applicationRegistration)) { + this.logger.warn( + `people-data-labs is installed but has no application registration, skipping upgrade for workspace ${workspaceId}`, + ); + + return; + } + + const installedVersion = semver.valid(application.version); + + if ( + isDefined(installedVersion) && + semver.gte(installedVersion, PEOPLE_DATA_LABS_TARGET_VERSION) + ) { + return; + } + + if (isDryRun) { + this.logger.log( + `[DRY RUN] Would upgrade people-data-labs from ${application.version} to ${PEOPLE_DATA_LABS_TARGET_VERSION} for workspace ${workspaceId}`, + ); + + return; + } + + try { + await this.applicationUpgradeService.upgradeApplication({ + appRegistrationId: applicationRegistration.id, + targetVersion: PEOPLE_DATA_LABS_TARGET_VERSION, + workspaceId, + }); + + this.logger.log( + `Upgraded people-data-labs from ${application.version} to ${PEOPLE_DATA_LABS_TARGET_VERSION} for workspace ${workspaceId}`, + ); + } catch (error) { + // Non-fatal: the app stays functional at runtime (view fields reference + // fields by database id); only a manifest re-sync of the stale version + // would fail, and the upgrade can be retried from the UI. + this.logger.error( + `Failed to upgrade people-data-labs for workspace ${workspaceId}: ${error}`, + ); + } + } +} diff --git a/packages/twenty-server/src/engine/core-modules/application/application-manifest/converters/__tests__/from-field-manifest-to-universal-flat-field-metadata.util.spec.ts b/packages/twenty-server/src/engine/core-modules/application/application-manifest/converters/__tests__/from-field-manifest-to-universal-flat-field-metadata.util.spec.ts index 05543e85c4..d5f09cee58 100644 --- a/packages/twenty-server/src/engine/core-modules/application/application-manifest/converters/__tests__/from-field-manifest-to-universal-flat-field-metadata.util.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/application/application-manifest/converters/__tests__/from-field-manifest-to-universal-flat-field-metadata.util.spec.ts @@ -106,6 +106,23 @@ describe('fromFieldManifestToUniversalFlatFieldMetadata', () => { }); }); + describe('system flags', () => { + it('never derives isSystem or isSystemSideEffect, even for reserved system field names', () => { + const result = fromFieldManifestToUniversalFlatFieldMetadata({ + fieldManifest: buildFieldManifest({ + type: FieldMetadataType.DATE_TIME, + name: 'createdAt', + label: 'Creation date', + }), + applicationUniversalIdentifier: APP_UID, + now: NOW, + }); + + expect(result.isSystem).toBe(false); + expect(result.isSystemSideEffect).toBe(false); + }); + }); + describe('isUIEditable', () => { it('defaults to true when omitted from the manifest', () => { const result = fromFieldManifestToUniversalFlatFieldMetadata({ diff --git a/packages/twenty-server/src/engine/core-modules/application/application-manifest/converters/from-field-manifest-to-universal-flat-field-metadata.util.ts b/packages/twenty-server/src/engine/core-modules/application/application-manifest/converters/from-field-manifest-to-universal-flat-field-metadata.util.ts index 9ef02d3529..c6774b7194 100644 --- a/packages/twenty-server/src/engine/core-modules/application/application-manifest/converters/from-field-manifest-to-universal-flat-field-metadata.util.ts +++ b/packages/twenty-server/src/engine/core-modules/application/application-manifest/converters/from-field-manifest-to-universal-flat-field-metadata.util.ts @@ -12,7 +12,6 @@ import { type CompositeFieldMetadataType } from 'src/engine/metadata-modules/fie import { generateDefaultValue } from 'src/engine/metadata-modules/field-metadata/utils/generate-default-value'; import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util'; import { nullifyEmptyCompositeDefaultValue } from 'src/engine/metadata-modules/flat-field-metadata/utils/nullify-empty-composite-default-value.util'; -import { PARTIAL_SYSTEM_FLAT_FIELD_METADATAS } from 'src/engine/metadata-modules/object-metadata/constants/partial-system-flat-field-metadatas.constant'; import { isMorphOrRelationFieldMetadataType } from 'src/engine/utils/is-morph-or-relation-field-metadata-type.util'; import { type UniversalFlatFieldMetadata } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-field-metadata.type'; @@ -68,9 +67,6 @@ export const fromFieldManifestToUniversalFlatFieldMetadata = ({ relationTargetObjectMetadataUniversalIdentifier, } = getRelationTargetUniversalIdentifiers(fieldManifest); - // TODO: generate system fields server-side from the object manifest - // so the converter doesn't need to re-normalize composite defaults - // that the SDK couldn't have known the canonical shape of. const rawDefaultValue = fieldManifest.defaultValue ?? generateDefaultValue(fieldManifest.type); const defaultValue = isCompositeFieldMetadataType(fieldManifest.type) @@ -93,9 +89,8 @@ export const fromFieldManifestToUniversalFlatFieldMetadata = ({ defaultValue, universalSettings: fieldManifest.universalSettings ?? null, isActive: true, - isSystem: fieldManifest.name in PARTIAL_SYSTEM_FLAT_FIELD_METADATAS, - isSystemSideEffect: - fieldManifest.name in PARTIAL_SYSTEM_FLAT_FIELD_METADATAS, + isSystem: false, + isSystemSideEffect: false, isUIEditable: fieldManifest.isUIEditable ?? true, isNullable: fieldManifest.isNullable ?? true, isUnique: fieldManifest.isUnique ?? false, diff --git a/packages/twenty-server/src/engine/core-modules/application/application-package/application-package.module.ts b/packages/twenty-server/src/engine/core-modules/application/application-package/application-package.module.ts index 646de25ab6..0e54aaaa53 100644 --- a/packages/twenty-server/src/engine/core-modules/application/application-package/application-package.module.ts +++ b/packages/twenty-server/src/engine/core-modules/application/application-package/application-package.module.ts @@ -8,14 +8,14 @@ import { FileStorageModule } from 'src/engine/core-modules/file-storage/file-sto import { FileEntity } from 'src/engine/core-modules/file/entities/file.entity'; import { SecureHttpClientModule } from 'src/engine/core-modules/secure-http-client/secure-http-client.module'; import { TwentyConfigModule } from 'src/engine/core-modules/twenty-config/twenty-config.module'; -import { UpgradeModule } from 'src/engine/core-modules/upgrade/upgrade.module'; +import { UpgradeStatusModule } from 'src/engine/core-modules/upgrade/upgrade-status.module'; @Module({ imports: [ FileStorageModule, SecureHttpClientModule, TwentyConfigModule, - UpgradeModule, + UpgradeStatusModule, TypeOrmModule.forFeature([FileEntity, ApplicationEntity]), ], providers: [ diff --git a/packages/twenty-server/src/engine/core-modules/upgrade/upgrade-status.module.ts b/packages/twenty-server/src/engine/core-modules/upgrade/upgrade-status.module.ts new file mode 100644 index 0000000000..de71601cf8 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/upgrade/upgrade-status.module.ts @@ -0,0 +1,35 @@ +import { Module } from '@nestjs/common'; +import { DiscoveryModule } from '@nestjs/core'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { CoreEntityCacheModule } from 'src/engine/core-entity-cache/core-entity-cache.module'; +import { UpgradeCommandRegistryService } from 'src/engine/core-modules/upgrade/services/upgrade-command-registry.service'; +import { UpgradeMigrationService } from 'src/engine/core-modules/upgrade/services/upgrade-migration.service'; +import { UpgradeSequenceReaderService } from 'src/engine/core-modules/upgrade/services/upgrade-sequence-reader.service'; +import { UpgradeStatusCacheService } from 'src/engine/core-modules/upgrade/services/upgrade-status-cache.service'; +import { UpgradeStatusService } from 'src/engine/core-modules/upgrade/services/upgrade-status.service'; +import { UpgradeMigrationEntity } from 'src/engine/core-modules/upgrade/upgrade-migration.entity'; +import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; + +@Module({ + imports: [ + CoreEntityCacheModule, + DiscoveryModule, + TypeOrmModule.forFeature([UpgradeMigrationEntity, WorkspaceEntity]), + ], + providers: [ + UpgradeCommandRegistryService, + UpgradeMigrationService, + UpgradeSequenceReaderService, + UpgradeStatusCacheService, + UpgradeStatusService, + ], + exports: [ + UpgradeCommandRegistryService, + UpgradeMigrationService, + UpgradeSequenceReaderService, + UpgradeStatusCacheService, + UpgradeStatusService, + ], +}) +export class UpgradeStatusModule {} diff --git a/packages/twenty-server/src/engine/core-modules/upgrade/upgrade.module.ts b/packages/twenty-server/src/engine/core-modules/upgrade/upgrade.module.ts index b57873efd5..85e46430e8 100644 --- a/packages/twenty-server/src/engine/core-modules/upgrade/upgrade.module.ts +++ b/packages/twenty-server/src/engine/core-modules/upgrade/upgrade.module.ts @@ -1,59 +1,39 @@ import { Module } from '@nestjs/common'; -import { DiscoveryModule } from '@nestjs/core'; -import { TypeOrmModule } from '@nestjs/typeorm'; import { WorkspaceIteratorModule } from 'src/database/commands/command-runners/workspace-iterator.module'; import { InstanceCommandProviderModule } from 'src/database/commands/upgrade-version-command/instance-command-provider.module'; import { WorkspaceCommandProviderModule } from 'src/database/commands/upgrade-version-command/workspace-command-provider.module'; -import { CoreEntityCacheModule } from 'src/engine/core-entity-cache/core-entity-cache.module'; import { MetricsModule } from 'src/engine/core-modules/metrics/metrics.module'; import { InstanceCommandRunnerService } from 'src/engine/core-modules/upgrade/services/instance-command-runner.service'; -import { UpgradeCommandRegistryService } from 'src/engine/core-modules/upgrade/services/upgrade-command-registry.service'; -import { UpgradeMigrationService } from 'src/engine/core-modules/upgrade/services/upgrade-migration.service'; -import { UpgradeSequenceReaderService } from 'src/engine/core-modules/upgrade/services/upgrade-sequence-reader.service'; import { UpgradeSequenceRunnerService } from 'src/engine/core-modules/upgrade/services/upgrade-sequence-runner.service'; -import { UpgradeStatusCacheService } from 'src/engine/core-modules/upgrade/services/upgrade-status-cache.service'; -import { UpgradeStatusService } from 'src/engine/core-modules/upgrade/services/upgrade-status.service'; import { WorkspaceCommandRunnerService } from 'src/engine/core-modules/upgrade/services/workspace-command-runner.service'; import { UpgradeGaugeService } from 'src/engine/core-modules/upgrade/upgrade-gauge.service'; -import { UpgradeMigrationEntity } from 'src/engine/core-modules/upgrade/upgrade-migration.entity'; -import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; +import { UpgradeStatusModule } from 'src/engine/core-modules/upgrade/upgrade-status.module'; import { UpgradeAwareEntityMetadataAdapter } from 'src/engine/twenty-orm/upgrade-aware/upgrade-aware-entity-metadata.adapter'; import { WorkspaceVersionModule } from 'src/engine/workspace-manager/workspace-version/workspace-version.module'; @Module({ imports: [ - CoreEntityCacheModule, - DiscoveryModule, InstanceCommandProviderModule, MetricsModule, + UpgradeStatusModule, WorkspaceCommandProviderModule, WorkspaceIteratorModule, WorkspaceVersionModule, - TypeOrmModule.forFeature([UpgradeMigrationEntity, WorkspaceEntity]), ], providers: [ - UpgradeMigrationService, InstanceCommandRunnerService, WorkspaceCommandRunnerService, - UpgradeCommandRegistryService, UpgradeAwareEntityMetadataAdapter, - UpgradeSequenceReaderService, UpgradeSequenceRunnerService, - UpgradeStatusService, - UpgradeStatusCacheService, UpgradeGaugeService, ], exports: [ - UpgradeMigrationService, + UpgradeStatusModule, InstanceCommandRunnerService, WorkspaceCommandRunnerService, - UpgradeCommandRegistryService, UpgradeAwareEntityMetadataAdapter, - UpgradeSequenceReaderService, UpgradeSequenceRunnerService, - UpgradeStatusService, - UpgradeStatusCacheService, ], }) export class UpgradeModule {} diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/constants/flat-field-metadata-editable-properties.constant.ts b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/constants/flat-field-metadata-editable-properties.constant.ts index 5a277421fe..6665b7e8a7 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/constants/flat-field-metadata-editable-properties.constant.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/constants/flat-field-metadata-editable-properties.constant.ts @@ -27,3 +27,7 @@ export const FLAT_FIELD_METADATA_EDITABLE_PROPERTIES = { 'standard' | 'custom', MetadataEntityPropertyName<'fieldMetadata'>[] >; + +export const FLAT_FIELD_METADATA_SYSTEM_SIDE_EFFECT_EDITABLE_PROPERTIES = [ + 'isActive', +] as const satisfies MetadataEntityPropertyName<'fieldMetadata'>[]; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/__tests__/generate-morph-or-relation-flat-field-metadata-pair.spec.ts b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/__tests__/generate-morph-or-relation-flat-field-metadata-pair.spec.ts index 852d7f2fee..dc6021daaf 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/__tests__/generate-morph-or-relation-flat-field-metadata-pair.spec.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/__tests__/generate-morph-or-relation-flat-field-metadata-pair.spec.ts @@ -70,7 +70,8 @@ describe('generate Morph Or Relation Flat Field Metadata Pair test suite', () => targetFlatObjectMetadata: PET_FLAT_OBJECT_MOCK, targetFlatFieldMetadataType: FieldMetadataType.RELATION, sourceFlatObjectMetadataJoinColumnName: 'petId', - flatApplication: MOCK_FLAT_APPLICATION, + applicationUniversalIdentifier: + MOCK_FLAT_APPLICATION.universalIdentifier, createFieldInput: { name: 'pets', label: 'Pets', @@ -104,7 +105,8 @@ describe('generate Morph Or Relation Flat Field Metadata Pair test suite', () => targetFlatObjectMetadata: PET_FLAT_OBJECT_MOCK, targetFlatFieldMetadataType: FieldMetadataType.RELATION, sourceFlatObjectMetadataJoinColumnName: 'petId', - flatApplication: MOCK_FLAT_APPLICATION, + applicationUniversalIdentifier: + MOCK_FLAT_APPLICATION.universalIdentifier, createFieldInput: { name: 'pets', label: 'Pets', @@ -138,7 +140,8 @@ describe('generate Morph Or Relation Flat Field Metadata Pair test suite', () => targetFlatObjectMetadata: PET_FLAT_OBJECT_MOCK, targetFlatFieldMetadataType: FieldMetadataType.RELATION, sourceFlatObjectMetadataJoinColumnName: 'petId', - flatApplication: MOCK_FLAT_APPLICATION, + applicationUniversalIdentifier: + MOCK_FLAT_APPLICATION.universalIdentifier, createFieldInput: { name: 'pets', label: 'Pets', @@ -172,7 +175,8 @@ describe('generate Morph Or Relation Flat Field Metadata Pair test suite', () => targetFlatObjectMetadata: PET_FLAT_OBJECT_MOCK, targetFlatFieldMetadataType: FieldMetadataType.RELATION, sourceFlatObjectMetadataJoinColumnName: 'targetPetId', - flatApplication: MOCK_FLAT_APPLICATION, + applicationUniversalIdentifier: + MOCK_FLAT_APPLICATION.universalIdentifier, morphId: '20202020-9a2b-4c3d-a4e5-f6a7b8c9d0e1', createFieldInput: { name: 'targetPet', @@ -207,7 +211,8 @@ describe('generate Morph Or Relation Flat Field Metadata Pair test suite', () => targetFlatObjectMetadata: PET_FLAT_OBJECT_MOCK, targetFlatFieldMetadataType: FieldMetadataType.RELATION, sourceFlatObjectMetadataJoinColumnName: 'targetPetId', - flatApplication: MOCK_FLAT_APPLICATION, + applicationUniversalIdentifier: + MOCK_FLAT_APPLICATION.universalIdentifier, morphId: '20202020-9a2b-4c3d-a4e5-f6a7b8c9d0e1', createFieldInput: { name: 'targetPet', @@ -242,7 +247,8 @@ describe('generate Morph Or Relation Flat Field Metadata Pair test suite', () => targetFlatObjectMetadata: PET_FLAT_OBJECT_MOCK, targetFlatFieldMetadataType: FieldMetadataType.MORPH_RELATION, sourceFlatObjectMetadataJoinColumnName: 'targetPetId', - flatApplication: MOCK_FLAT_APPLICATION, + applicationUniversalIdentifier: + MOCK_FLAT_APPLICATION.universalIdentifier, morphId: '20202020-9a2b-4c3d-a4e5-f6a7b8c9d0e1', createFieldInput: { name: 'targetPet', @@ -353,7 +359,8 @@ describe('generate Morph Or Relation Flat Field Metadata Pair test suite', () => targetFlatObjectMetadata: PET_FLAT_OBJECT_MOCK, targetFlatFieldMetadataType: FieldMetadataType.RELATION, sourceFlatObjectMetadataJoinColumnName: 'petId', - flatApplication: MOCK_FLAT_APPLICATION, + applicationUniversalIdentifier: + MOCK_FLAT_APPLICATION.universalIdentifier, createFieldInput: { name: 'pets', label: 'Pets', diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/compute-flat-field-to-update-from-morph-relation-update-payload.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/compute-flat-field-to-update-from-morph-relation-update-payload.util.ts index 75a612f3dc..be550feb24 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/compute-flat-field-to-update-from-morph-relation-update-payload.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/compute-flat-field-to-update-from-morph-relation-update-payload.util.ts @@ -113,7 +113,7 @@ export const computeFlatFieldToUpdateFromMorphRelationUpdatePayload = ({ sourceFlatObjectMetadata: sourceObjectMetadata, targetFlatObjectMetadata: newTargetObjectMetadata, targetFlatFieldMetadataType: FieldMetadataType.RELATION, - flatApplication, + applicationUniversalIdentifier: flatApplication.universalIdentifier, sourceFlatObjectMetadataJoinColumnName: computeMorphOrRelationFieldJoinColumnName({ name: computedMorphName, diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-create-field-input-to-flat-field-metadatas-to-create.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-create-field-input-to-flat-field-metadatas-to-create.util.ts index 978ea99587..9dd58ee60e 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-create-field-input-to-flat-field-metadatas-to-create.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-create-field-input-to-flat-field-metadatas-to-create.util.ts @@ -76,7 +76,7 @@ export const fromCreateFieldInputToFlatFieldMetadatasToCreate = async ({ const commonFlatFieldMetadata = getDefaultFlatFieldMetadata({ createFieldInput, - flatApplication, + applicationUniversalIdentifier: flatApplication.universalIdentifier, objectMetadataUniversalIdentifier: parentFlatObjectMetadata.universalIdentifier, }); diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-delete-field-input-to-flat-field-metadatas-to-delete.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-delete-field-input-to-flat-field-metadatas-to-delete.util.ts index 57cf61bfb8..ec3c9efb41 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-delete-field-input-to-flat-field-metadatas-to-delete.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-delete-field-input-to-flat-field-metadatas-to-delete.util.ts @@ -66,6 +66,13 @@ export const fromDeleteFieldInputToFlatFieldMetadatasToDelete = ({ ); } + if (flatFieldMetadataToDelete.isSystemSideEffect === true) { + throw new FieldMetadataException( + `Cannot delete system-managed field "${flatFieldMetadataToDelete.name}"`, + FieldMetadataExceptionCode.FIELD_MUTATION_NOT_ALLOWED, + ); + } + const flatObjectMetadata = findFlatEntityByIdInFlatEntityMaps({ flatEntityId: flatFieldMetadataToDelete.objectMetadataId, flatEntityMaps: existingFlatObjectMetadataMaps, diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-morph-relation-create-field-input-to-flat-field-metadatas.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-morph-relation-create-field-input-to-flat-field-metadatas.util.ts index 67ae08c1f1..5470304eb1 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-morph-relation-create-field-input-to-flat-field-metadatas.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-morph-relation-create-field-input-to-flat-field-metadatas.util.ts @@ -110,7 +110,7 @@ export const fromMorphRelationCreateFieldInputToFlatFieldMetadatas = async ({ targetFlatObjectMetadata, targetFlatFieldMetadataType: FieldMetadataType.RELATION, morphId, - flatApplication, + applicationUniversalIdentifier: flatApplication.universalIdentifier, junctionTargetFlatFieldMetadata, }); diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-relation-create-field-input-to-flat-field-metadatas.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-relation-create-field-input-to-flat-field-metadatas.util.ts index 2e4ef63328..48c114133c 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-relation-create-field-input-to-flat-field-metadatas.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-relation-create-field-input-to-flat-field-metadatas.util.ts @@ -87,7 +87,7 @@ export const fromRelationCreateFieldInputToFlatFieldMetadatas = async ({ sourceFlatObjectMetadata, targetFlatObjectMetadata, targetFlatFieldMetadataType: FieldMetadataType.RELATION, - flatApplication, + applicationUniversalIdentifier: flatApplication.universalIdentifier, junctionTargetFlatFieldMetadata, }); diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/generate-morph-or-relation-flat-field-metadata-pair.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/generate-morph-or-relation-flat-field-metadata-pair.util.ts index 511d35a760..31910959c9 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/generate-morph-or-relation-flat-field-metadata-pair.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/generate-morph-or-relation-flat-field-metadata-pair.util.ts @@ -7,10 +7,9 @@ import { import { v4 } from 'uuid'; -import { computeMorphOrRelationFieldJoinColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-morph-or-relation-field-join-column-name.util'; -import { type FlatApplication } from 'src/engine/core-modules/application/types/flat-application.type'; import { type CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input'; import { type MorphOrRelationFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/types/morph-or-relation-field-metadata-type.type'; +import { computeMorphOrRelationFieldJoinColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-morph-or-relation-field-join-column-name.util'; import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type'; import { generateIndexForFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/generate-index-for-flat-field-metadata.util'; import { getDefaultFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/get-default-flat-field-metadata-from-create-field-input.util'; @@ -59,11 +58,12 @@ type GenerateMorphOrRelationFlatFieldMetadataPairArgs = { Pick > & { type: MorphOrRelationFieldMetadataType }; morphId?: string | null; - flatApplication: FlatApplication; + applicationUniversalIdentifier: string; targetFieldName?: string; junctionTargetFlatFieldMetadata?: FlatFieldMetadata; sourceFieldUniversalIdentifier?: string; targetFieldUniversalIdentifier?: string; + isSystemSideEffect?: boolean; }; export type SourceTargetMorphOrRelationFlatFieldAndFlatIndex = { @@ -76,13 +76,14 @@ export const generateMorphOrRelationFlatFieldMetadataPair = ({ sourceFlatObjectMetadata, targetFlatObjectMetadata, targetFlatFieldMetadataType, - flatApplication, + applicationUniversalIdentifier, sourceFlatObjectMetadataJoinColumnName, morphId = null, targetFieldName, junctionTargetFlatFieldMetadata, sourceFieldUniversalIdentifier: providedSourceFieldUniversalIdentifier, targetFieldUniversalIdentifier: providedTargetFieldUniversalIdentifier, + isSystemSideEffect = false, }: GenerateMorphOrRelationFlatFieldMetadataPairArgs): SourceTargetMorphOrRelationFlatFieldAndFlatIndex => { const sourceFlatFieldMetadataType = createFieldInput.type; @@ -119,9 +120,10 @@ export const generateMorphOrRelationFlatFieldMetadataPair = ({ createFieldInput.universalIdentifier ?? sourceFieldUniversalIdentifier, }, - flatApplication, + applicationUniversalIdentifier, objectMetadataUniversalIdentifier: sourceFlatObjectMetadata.universalIdentifier, + isSystemSideEffect, }), morphId: sourceFlatFieldMetadataType === FieldMetadataType.MORPH_RELATION @@ -175,9 +177,10 @@ export const generateMorphOrRelationFlatFieldMetadataPair = ({ ...targetCreateFieldInput, universalIdentifier: targetFieldUniversalIdentifier, }, - flatApplication, + applicationUniversalIdentifier, objectMetadataUniversalIdentifier: targetFlatObjectMetadata.universalIdentifier, + isSystemSideEffect, }), morphId: targetFlatFieldMetadataType === FieldMetadataType.MORPH_RELATION diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/get-default-flat-field-metadata-from-create-field-input.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/get-default-flat-field-metadata-from-create-field-input.util.ts index e473bb011e..cf0898bb14 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/get-default-flat-field-metadata-from-create-field-input.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/get-default-flat-field-metadata-from-create-field-input.util.ts @@ -4,7 +4,6 @@ import { } from 'twenty-shared/utils'; import { v4 } from 'uuid'; -import { type FlatApplication } from 'src/engine/core-modules/application/types/flat-application.type'; import { type CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input'; import { generateDefaultValue } from 'src/engine/metadata-modules/field-metadata/utils/generate-default-value'; import { generateNullable } from 'src/engine/metadata-modules/field-metadata/utils/generate-nullable'; @@ -14,13 +13,15 @@ import { type UniversalFlatFieldMetadata } from 'src/engine/workspace-manager/wo type GetDefaultFlatFieldMetadataArgs = { createFieldInput: Omit; - flatApplication: FlatApplication; + applicationUniversalIdentifier: string; objectMetadataUniversalIdentifier: string; + isSystemSideEffect?: boolean; }; export const getDefaultFlatFieldMetadata = ({ createFieldInput, - flatApplication, + applicationUniversalIdentifier, objectMetadataUniversalIdentifier, + isSystemSideEffect = false, }: GetDefaultFlatFieldMetadataArgs) => { const { defaultValue, settings } = extractAndSanitizeObjectStringFields( createFieldInput, @@ -41,7 +42,7 @@ export const getDefaultFlatFieldMetadata = ({ createFieldInput.isRemoteCreation, ), isSystem: createFieldInput.isSystem ?? false, - isSystemSideEffect: false, + isSystemSideEffect, isUnique: createFieldInput.isUnique ?? false, label: createFieldInput.label, name: createFieldInput.name, @@ -65,7 +66,7 @@ export const getDefaultFlatFieldMetadata = ({ ? !createFieldInput.isUIReadOnly : true), morphId: null, - applicationUniversalIdentifier: flatApplication.universalIdentifier, + applicationUniversalIdentifier, objectMetadataUniversalIdentifier, relationTargetObjectMetadataUniversalIdentifier: null, relationTargetFieldMetadataUniversalIdentifier: null, diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/sanitize-raw-update-field-input.ts b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/sanitize-raw-update-field-input.ts index c2f0f9bcec..590394b3df 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/sanitize-raw-update-field-input.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/sanitize-raw-update-field-input.ts @@ -9,11 +9,14 @@ import { FieldMetadataException, FieldMetadataExceptionCode, } from 'src/engine/metadata-modules/field-metadata/field-metadata.exception'; +import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util'; import { ALL_OVERRIDABLE_PROPERTIES_BY_METADATA_NAME } from 'src/engine/metadata-modules/flat-entity/constant/all-overridable-properties-by-metadata-name.constant'; -import { FLAT_FIELD_METADATA_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-field-metadata/constants/flat-field-metadata-editable-properties.constant'; +import { + FLAT_FIELD_METADATA_EDITABLE_PROPERTIES, + FLAT_FIELD_METADATA_SYSTEM_SIDE_EFFECT_EDITABLE_PROPERTIES, +} from 'src/engine/metadata-modules/flat-field-metadata/constants/flat-field-metadata-editable-properties.constant'; import { type FlatFieldMetadataEditableProperties } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata-editable-properties.constant'; import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type'; -import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util'; import { nullifyEmptyCompositeDefaultValue } from 'src/engine/metadata-modules/flat-field-metadata/utils/nullify-empty-composite-default-value.util'; import { belongsToTwentyStandardApp } from 'src/engine/metadata-modules/utils/belongs-to-twenty-standard-app.util'; import { computeMetadataOverridesBlob } from 'src/engine/metadata-modules/utils/compute-metadata-overrides-blob.util'; @@ -39,6 +42,27 @@ export const sanitizeRawUpdateFieldInput = ({ ], ); + if (existingFlatFieldMetadata.isSystemSideEffect === true && !isSystemBuild) { + const forbiddenUpdatedProperties = [ + ...Object.keys(updatedEditableFieldProperties), + ...(isDefined(rawUpdateFieldInput.morphRelationsUpdatePayload) + ? ['morphRelationsUpdatePayload'] + : []), + ].filter( + (property) => + !FLAT_FIELD_METADATA_SYSTEM_SIDE_EFFECT_EDITABLE_PROPERTIES.includes( + property as (typeof FLAT_FIELD_METADATA_SYSTEM_SIDE_EFFECT_EDITABLE_PROPERTIES)[number], + ), + ); + + if (forbiddenUpdatedProperties.length > 0) { + throw new FieldMetadataException( + `Cannot edit system-managed field "${existingFlatFieldMetadata.name}" properties: ${forbiddenUpdatedProperties.join(', ')}`, + FieldMetadataExceptionCode.FIELD_MUTATION_NOT_ALLOWED, + ); + } + } + updatedEditableFieldProperties.options = !isDefined( updatedEditableFieldProperties.options, ) diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/from-create-object-input-to-flat-object-metadata-and-flat-field-metadatas-to-create.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/from-create-object-input-to-flat-object-metadata-and-flat-field-metadatas-to-create.util.ts index 1a393692a8..77507d717c 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/from-create-object-input-to-flat-object-metadata-and-flat-field-metadatas-to-create.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/from-create-object-input-to-flat-object-metadata-and-flat-field-metadatas-to-create.util.ts @@ -7,30 +7,24 @@ import { import { v4 } from 'uuid'; import { type FlatApplication } from 'src/engine/core-modules/application/types/flat-application.type'; -import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type'; import { type CreateObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/create-object.input'; import { buildNameFlatFieldMetadataForCustomObject } from 'src/engine/metadata-modules/object-metadata/utils/build-name-flat-field-metadata-for-custom-object.util'; -import { buildDefaultRelationFlatFieldMetadatasForCustomObject } from 'src/engine/metadata-modules/object-metadata/utils/build-default-relation-flat-field-metadatas-for-custom-object.util'; import { type UniversalFlatFieldMetadata } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-field-metadata.type'; -import { type UniversalFlatIndexMetadata } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-index-metadata.type'; import { type UniversalFlatObjectMetadata } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-object-metadata.type'; type FromCreateObjectInputToFlatObjectMetadataAndFlatFieldMetadatasToCreateArgs = { createObjectInput: CreateObjectInput; flatApplication: FlatApplication; - } & Pick; + }; export const fromCreateObjectInputToFlatObjectMetadataAndFlatFieldMetadatasToCreate = ({ createObjectInput: rawCreateObjectInput, flatApplication, - flatObjectMetadataMaps: existingFlatObjectMetadataMaps, }: FromCreateObjectInputToFlatObjectMetadataAndFlatFieldMetadatasToCreateArgs): { flatObjectMetadataToCreate: UniversalFlatObjectMetadata & { id: string }; flatFieldMetadataToCreateOnObject: UniversalFlatFieldMetadata[]; - relationTargetFlatFieldMetadataToCreate: UniversalFlatFieldMetadata[]; - flatIndexMetadataToCreate: UniversalFlatIndexMetadata[]; } => { const createObjectInput = trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties( @@ -105,25 +99,11 @@ export const fromCreateObjectInputToFlatObjectMetadataAndFlatFieldMetadatasToCre }, }); - const { - standardSourceFlatFieldMetadatas, - standardTargetFlatFieldMetadatas, - standardTargetFlatIndexMetadatas, - } = buildDefaultRelationFlatFieldMetadatasForCustomObject({ - existingFlatObjectMetadataMaps, - sourceFlatObjectMetadata: universalFlatObjectMetadataToCreate, - flatApplication, - }); - - const flatFieldMetadataToCreateOnObject: UniversalFlatFieldMetadata[] = [ - ...(isDefined(nameFlatFieldMetadata) ? [nameFlatFieldMetadata] : []), - ...standardSourceFlatFieldMetadatas, - ]; + const flatFieldMetadataToCreateOnObject: UniversalFlatFieldMetadata[] = + isDefined(nameFlatFieldMetadata) ? [nameFlatFieldMetadata] : []; return { flatObjectMetadataToCreate: universalFlatObjectMetadataToCreate, flatFieldMetadataToCreateOnObject, - relationTargetFlatFieldMetadataToCreate: standardTargetFlatFieldMetadatas, - flatIndexMetadataToCreate: standardTargetFlatIndexMetadatas, }; }; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/from-delete-object-input-to-flat-field-metadatas-to-delete.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/from-delete-object-input-to-flat-field-metadatas-to-delete.util.ts index fba3ea96f2..514545351b 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/from-delete-object-input-to-flat-field-metadatas-to-delete.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/from-delete-object-input-to-flat-field-metadatas-to-delete.util.ts @@ -59,6 +59,10 @@ export const fromDeleteObjectInputToFlatFieldMetadatasToDelete = ({ }); const flatFieldMetadatasToDelete = objectFlatFieldMetadatas.flatMap( (flatFieldMetadata) => { + if (flatFieldMetadata.isSystemSideEffect === true) { + return []; + } + if ( isMorphOrRelationFlatFieldMetadata(flatFieldMetadata) && flatFieldMetadata.relationTargetObjectMetadataId !== @@ -87,6 +91,10 @@ export const fromDeleteObjectInputToFlatFieldMetadatasToDelete = ({ ).filter( (flatIndex): flatIndex is FlatIndexMetadata => isDefined(flatIndex) && + !( + flatIndex.isSystemSideEffect === true && + flatIndex.objectMetadataId === flatObjectMetadataToDelete.id + ) && (flatIndex.objectMetadataId === flatObjectMetadataToDelete.id || flatIndex.flatIndexFieldMetadatas.some((flatIndexField) => fieldIdsToDelete.has(flatIndexField.fieldMetadataId), diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/handle-flat-object-metadata-update-side-effect.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/handle-flat-object-metadata-update-side-effect.util.ts index e90294edd1..a1d754fc3d 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/handle-flat-object-metadata-update-side-effect.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/handle-flat-object-metadata-update-side-effect.util.ts @@ -53,6 +53,7 @@ export const handleFlatObjectMetadataUpdateSideEffect = ({ toFlatObjectMetadata, flatObjectMetadataMaps, flatIndexMaps, + systemSideEffectMorphFieldsOnly: false, }) : { morphRelatedFlatIndexesToUpdate: [], diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/rename-related-morph-field-on-object-names-update.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/rename-related-morph-field-on-object-names-update.util.ts index 1f58d68a37..c139ffaadb 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/rename-related-morph-field-on-object-names-update.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/rename-related-morph-field-on-object-names-update.util.ts @@ -3,6 +3,7 @@ import { type FieldMetadataType, type FromTo, } from 'twenty-shared/types'; +import { capitalize } from 'twenty-shared/utils'; import { computeMorphOrRelationFieldJoinColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-morph-or-relation-field-join-column-name.util'; import { computeMorphRelationFlatFieldName } from 'src/engine/metadata-modules/field-metadata/utils/compute-morph-relation-flat-field-name.util'; @@ -52,9 +53,15 @@ const updateMorphFlatFieldName = ({ }) : undefined; + const newLabel = + fromMorphFlatFieldMetadata.isSystemSideEffect === true + ? capitalize(toRelationTargetFlatObjectMetadata.nameSingular) + : fromMorphFlatFieldMetadata.label; + return { ...fromMorphFlatFieldMetadata, name: newMorphFieldName, + label: newLabel, universalSettings: { ...fromMorphFlatFieldMetadata.universalSettings, joinColumnName: newJoinColumnName, @@ -69,7 +76,9 @@ type RenameRelatedMorphFieldOnObjectNamesUpdateArgs = FromTo< Pick< AllFlatEntityMaps, 'flatFieldMetadataMaps' | 'flatObjectMetadataMaps' | 'flatIndexMaps' - >; + > & { + systemSideEffectMorphFieldsOnly: boolean; + }; type RenameRelatedMorphFieldOnObjectNamesUpdateReturnType = { morphFlatFieldMetadatasToUpdate: UniversalFlatFieldMetadata[]; @@ -81,6 +90,7 @@ export const renameRelatedMorphFieldOnObjectNamesUpdate = ({ flatFieldMetadataMaps, flatObjectMetadataMaps, flatIndexMaps, + systemSideEffectMorphFieldsOnly, }: RenameRelatedMorphFieldOnObjectNamesUpdateArgs): RenameRelatedMorphFieldOnObjectNamesUpdateReturnType => { const objectFlatFieldMetadatas = findManyFlatEntityByIdInFlatEntityMapsOrThrow({ @@ -92,7 +102,11 @@ export const renameRelatedMorphFieldOnObjectNamesUpdate = ({ getFlatObjectMetadataTargetMorphRelationFlatFieldMetadatasOrThrow({ flatFieldMetadataMaps, objectFlatFieldMetadatas, - }); + }).filter( + (morphFlatFieldMetadata) => + (morphFlatFieldMetadata.isSystemSideEffect === true) === + systemSideEffectMorphFieldsOnly, + ); const initialAccumulator: RenameRelatedMorphFieldOnObjectNamesUpdateReturnType = { diff --git a/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/metadata-side-effect-handlers.module.ts b/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/metadata-side-effect-handlers.module.ts index cd38954764..8e8102afd7 100644 --- a/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/metadata-side-effect-handlers.module.ts +++ b/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/metadata-side-effect-handlers.module.ts @@ -7,6 +7,8 @@ import { FieldUniqueBackingIndexOnUpdateSideEffectHandlerService } from 'src/eng import { ObjectSearchVectorOnCreateSideEffectHandlerService } from 'src/engine/metadata-modules/metadata-side-effect/handlers/object-metadata/services/object-search-vector-on-create-side-effect-handler.service'; import { ObjectSearchVectorOnUpdateSideEffectHandlerService } from 'src/engine/metadata-modules/metadata-side-effect/handlers/object-metadata/services/object-search-vector-on-update-side-effect-handler.service'; import { ObjectSystemFieldsOnCreateSideEffectHandlerService } from 'src/engine/metadata-modules/metadata-side-effect/handlers/object-metadata/services/object-system-fields-on-create-side-effect-handler.service'; +import { ObjectSystemRelationsOnCreateSideEffectHandlerService } from 'src/engine/metadata-modules/metadata-side-effect/handlers/object-metadata/services/object-system-relations-on-create-side-effect-handler.service'; +import { ObjectSystemRelationsOnUpdateSideEffectHandlerService } from 'src/engine/metadata-modules/metadata-side-effect/handlers/object-metadata/services/object-system-relations-on-update-side-effect-handler.service'; import { ObjectSystemSideEffectsOnDeleteSideEffectHandlerService } from 'src/engine/metadata-modules/metadata-side-effect/handlers/object-metadata/services/object-system-side-effects-on-delete-side-effect-handler.service'; @Module({ @@ -16,6 +18,8 @@ import { ObjectSystemSideEffectsOnDeleteSideEffectHandlerService } from 'src/eng FieldUniqueBackingIndexOnDeleteSideEffectHandlerService, FieldSearchFieldMetadataOnDeleteSideEffectHandlerService, ObjectSystemFieldsOnCreateSideEffectHandlerService, + ObjectSystemRelationsOnCreateSideEffectHandlerService, + ObjectSystemRelationsOnUpdateSideEffectHandlerService, ObjectSearchVectorOnCreateSideEffectHandlerService, ObjectSearchVectorOnUpdateSideEffectHandlerService, ObjectSystemSideEffectsOnDeleteSideEffectHandlerService, diff --git a/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/object-metadata/services/__tests__/object-system-relations-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-relations-on-create-side-effect-handler.service.spec.ts new file mode 100644 index 0000000000..88fcea4439 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/object-metadata/services/__tests__/object-system-relations-on-create-side-effect-handler.service.spec.ts @@ -0,0 +1,206 @@ +import { getSystemRelationFieldUniversalIdentifier } from 'twenty-shared/application'; +import { + DEFAULT_RELATIONS_OBJECTS_STANDARD_IDS, + STANDARD_OBJECTS, +} from 'twenty-shared/metadata'; + +import { type AllFlatEntityOperationRecordByMetadataName } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-operation-record-by-metadata-name.type'; +import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/get-flat-object-metadata.mock'; +import { type BuildSideEffectsArgs } from 'src/engine/metadata-modules/metadata-side-effect/interfaces/base-metadata-side-effect-handler.service'; +import { ObjectSystemRelationsOnCreateSideEffectHandlerService } from 'src/engine/metadata-modules/metadata-side-effect/handlers/object-metadata/services/object-system-relations-on-create-side-effect-handler.service'; + +const APPLICATION_UNIVERSAL_IDENTIFIER = 'a1a2a3a4-a5a6-4000-8000-000000000001'; +const SOURCE_OBJECT_UNIVERSAL_IDENTIFIER = + 'b1b2b3b4-b5b6-4000-8000-000000000001'; + +const NAME_PLURAL_BY_STANDARD_OBJECT_NAME_SINGULAR: Record = { + timelineActivity: 'timelineActivities', + attachment: 'attachments', + noteTarget: 'noteTargets', + taskTarget: 'taskTargets', +}; + +// The handler resolves the standard relation objects by their pinned standard +// universal identifiers, so the mocks must carry them. +const buildStandardTargetFlatObjectMetadatas = () => + DEFAULT_RELATIONS_OBJECTS_STANDARD_IDS.map((nameSingular) => + getFlatObjectMetadataMock({ + universalIdentifier: STANDARD_OBJECTS[nameSingular].universalIdentifier, + nameSingular, + namePlural: NAME_PLURAL_BY_STANDARD_OBJECT_NAME_SINGULAR[nameSingular], + labelSingular: nameSingular, + labelPlural: NAME_PLURAL_BY_STANDARD_OBJECT_NAME_SINGULAR[nameSingular], + }), + ); + +const SOURCE_FLAT_OBJECT_METADATA = getFlatObjectMetadataMock({ + universalIdentifier: SOURCE_OBJECT_UNIVERSAL_IDENTIFIER, + applicationUniversalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER, + nameSingular: 'rocket', + namePlural: 'rockets', + labelSingular: 'Rocket', + labelPlural: 'Rockets', +}); + +const buildArgs = ({ + standardTargetFlatObjectMetadatas = buildStandardTargetFlatObjectMetadatas(), + callerFieldMetadataToCreateByUniversalIdentifier = {}, + existingFieldMetadataByUniversalIdentifier = {}, +}: { + standardTargetFlatObjectMetadatas?: ReturnType< + typeof buildStandardTargetFlatObjectMetadatas + >; + callerFieldMetadataToCreateByUniversalIdentifier?: Record; + existingFieldMetadataByUniversalIdentifier?: Record; +}): BuildSideEffectsArgs<'objectMetadata'> => { + const objectByUniversalIdentifier: Record = {}; + + for (const flatObjectMetadata of standardTargetFlatObjectMetadatas) { + objectByUniversalIdentifier[flatObjectMetadata.universalIdentifier] = + flatObjectMetadata; + } + + return { + flatEntity: SOURCE_FLAT_OBJECT_METADATA, + allFlatEntityOperationRecordByMetadataName: { + fieldMetadata: { + flatEntityToCreate: callerFieldMetadataToCreateByUniversalIdentifier, + }, + } as unknown as AllFlatEntityOperationRecordByMetadataName, + relatedFlatEntityMaps: { + flatObjectMetadataMaps: { + byUniversalIdentifier: objectByUniversalIdentifier, + }, + flatFieldMetadataMaps: { + byUniversalIdentifier: existingFieldMetadataByUniversalIdentifier, + }, + }, + context: {}, + } as unknown as BuildSideEffectsArgs<'objectMetadata'>; +}; + +describe('ObjectSystemRelationsOnCreateSideEffectHandlerService', () => { + const handler = + new (ObjectSystemRelationsOnCreateSideEffectHandlerService as unknown as new () => ObjectSystemRelationsOnCreateSideEffectHandlerService)(); + + const targets = buildStandardTargetFlatObjectMetadatas(); + + const forwardUniversalIdentifierFor = ( + standardObjectUniversalIdentifier: string, + ) => + getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: SOURCE_OBJECT_UNIVERSAL_IDENTIFIER, + relationTargetObjectUniversalIdentifier: + standardObjectUniversalIdentifier, + }); + + const reverseUniversalIdentifierFor = (objectUniversalIdentifier: string) => + getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier, + relationTargetObjectUniversalIdentifier: + SOURCE_OBJECT_UNIVERSAL_IDENTIFIER, + }); + + it('should provision a forward + reverse (both name-free, isSystemSideEffect) field pair and its index for all four standard relation objects', () => { + const result = handler.buildSideEffects(buildArgs({})); + + expect(result.status).toBe('success'); + + if (result.status !== 'success') { + throw new Error('expected success'); + } + + const fieldMetadataToCreate = + result.operations.fieldMetadata?.flatEntityToCreate ?? {}; + const indexToCreate = result.operations.index?.flatEntityToCreate ?? {}; + + expect(Object.keys(fieldMetadataToCreate)).toHaveLength( + DEFAULT_RELATIONS_OBJECTS_STANDARD_IDS.length * 2, + ); + expect(Object.keys(indexToCreate)).toHaveLength( + DEFAULT_RELATIONS_OBJECTS_STANDARD_IDS.length, + ); + + for (const target of targets) { + const forwardUniversalIdentifier = forwardUniversalIdentifierFor( + target.universalIdentifier, + ); + const reverseUniversalIdentifier = reverseUniversalIdentifierFor( + target.universalIdentifier, + ); + + const forwardField = fieldMetadataToCreate[forwardUniversalIdentifier]; + const reverseField = fieldMetadataToCreate[reverseUniversalIdentifier]; + + expect(forwardField).toBeDefined(); + expect(reverseField).toBeDefined(); + expect(forwardField?.isSystemSideEffect).toBe(true); + expect(reverseField?.isSystemSideEffect).toBe(true); + expect( + reverseField?.relationTargetObjectMetadataUniversalIdentifier, + ).toBe(SOURCE_OBJECT_UNIVERSAL_IDENTIFIER); + } + }); + + // The handler always emits its bundles: a caller field colliding on + // universal identifier is not skipped but hard-fails at the engine merge + // (RESERVED_SYSTEM_UNIVERSAL_IDENTIFIER collision). + it('should emit all bundles even when a caller field collides on universal identifier', () => { + const collidingTarget = targets[0]; + const collidingForwardUniversalIdentifier = forwardUniversalIdentifierFor( + collidingTarget.universalIdentifier, + ); + + const result = handler.buildSideEffects( + buildArgs({ + callerFieldMetadataToCreateByUniversalIdentifier: { + [collidingForwardUniversalIdentifier]: { + universalIdentifier: collidingForwardUniversalIdentifier, + }, + }, + }), + ); + + expect(result.status).toBe('success'); + + if (result.status !== 'success') { + throw new Error('expected success'); + } + + const fieldMetadataToCreate = + result.operations.fieldMetadata?.flatEntityToCreate ?? {}; + const indexToCreate = result.operations.index?.flatEntityToCreate ?? {}; + + expect(Object.keys(fieldMetadataToCreate)).toHaveLength( + DEFAULT_RELATIONS_OBJECTS_STANDARD_IDS.length * 2, + ); + expect(Object.keys(indexToCreate)).toHaveLength( + DEFAULT_RELATIONS_OBJECTS_STANDARD_IDS.length, + ); + expect( + fieldMetadataToCreate[collidingForwardUniversalIdentifier], + ).toBeDefined(); + }); + + it('should fail with one error per missing standard relation object', () => { + const [, , ...standardTargetFlatObjectMetadatasMissingFirstTwo] = + buildStandardTargetFlatObjectMetadatas(); + + const result = handler.buildSideEffects( + buildArgs({ + standardTargetFlatObjectMetadatas: + standardTargetFlatObjectMetadatasMissingFirstTwo, + }), + ); + + expect(result.status).toBe('fail'); + + if (result.status !== 'fail') { + throw new Error('expected fail'); + } + + expect(result.errors).toHaveLength(2); + }); +}); diff --git a/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/object-metadata/services/__tests__/object-system-relations-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-system-relations-on-update-side-effect-handler.service.spec.ts new file mode 100644 index 0000000000..386ff2797a --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/object-metadata/services/__tests__/object-system-relations-on-update-side-effect-handler.service.spec.ts @@ -0,0 +1,93 @@ +import { type AllFlatEntityOperationRecordByMetadataName } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-operation-record-by-metadata-name.type'; +import { getFlatObjectMetadataMock } from 'src/engine/metadata-modules/flat-object-metadata/__mocks__/get-flat-object-metadata.mock'; +import { type BuildSideEffectsArgs } from 'src/engine/metadata-modules/metadata-side-effect/interfaces/base-metadata-side-effect-handler.service'; +import { ObjectSystemRelationsOnUpdateSideEffectHandlerService } from 'src/engine/metadata-modules/metadata-side-effect/handlers/object-metadata/services/object-system-relations-on-update-side-effect-handler.service'; + +const OBJECT_UNIVERSAL_IDENTIFIER = 'b1b2b3b4-b5b6-4000-8000-000000000001'; + +const buildArgs = ({ + updatedNameSingular, + updatedNamePlural, + existingNameSingular = 'rocket', + existingNamePlural = 'rockets', + includeExisting = true, +}: { + updatedNameSingular: string; + updatedNamePlural: string; + existingNameSingular?: string; + existingNamePlural?: string; + includeExisting?: boolean; +}): BuildSideEffectsArgs<'objectMetadata'> => { + const existingFlatObjectMetadata = getFlatObjectMetadataMock({ + universalIdentifier: OBJECT_UNIVERSAL_IDENTIFIER, + nameSingular: existingNameSingular, + namePlural: existingNamePlural, + labelSingular: existingNameSingular, + labelPlural: existingNamePlural, + }); + + const updatedFlatObjectMetadata = getFlatObjectMetadataMock({ + universalIdentifier: OBJECT_UNIVERSAL_IDENTIFIER, + nameSingular: updatedNameSingular, + namePlural: updatedNamePlural, + labelSingular: updatedNameSingular, + labelPlural: updatedNamePlural, + }); + + return { + flatEntity: updatedFlatObjectMetadata, + allFlatEntityOperationRecordByMetadataName: + {} as unknown as AllFlatEntityOperationRecordByMetadataName, + relatedFlatEntityMaps: { + flatObjectMetadataMaps: { + byUniversalIdentifier: includeExisting + ? { [OBJECT_UNIVERSAL_IDENTIFIER]: existingFlatObjectMetadata } + : {}, + }, + flatFieldMetadataMaps: { byUniversalIdentifier: {} }, + flatIndexMaps: { byUniversalIdentifier: {} }, + }, + context: {}, + } as unknown as BuildSideEffectsArgs<'objectMetadata'>; +}; + +describe('ObjectSystemRelationsOnUpdateSideEffectHandlerService', () => { + const handler = + new (ObjectSystemRelationsOnUpdateSideEffectHandlerService as unknown as new () => ObjectSystemRelationsOnUpdateSideEffectHandlerService)(); + + it('should noop when the object name is unchanged', () => { + const result = handler.buildSideEffects( + buildArgs({ + existingNameSingular: 'rocket', + existingNamePlural: 'rockets', + updatedNameSingular: 'rocket', + updatedNamePlural: 'rockets', + }), + ); + + expect(result.status).toBe('noop'); + }); + + it('should fail when the existing object cannot be resolved from the maps', () => { + const result = handler.buildSideEffects( + buildArgs({ + includeExisting: false, + updatedNameSingular: 'house', + updatedNamePlural: 'houses', + }), + ); + + expect(result.status).toBe('fail'); + }); + + it('should noop when a renamed object has no system side-effect morph relations to rename', () => { + const result = handler.buildSideEffects( + buildArgs({ + updatedNameSingular: 'house', + updatedNamePlural: 'houses', + }), + ); + + expect(result.status).toBe('noop'); + }); +}); diff --git a/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/object-metadata/services/__tests__/object-system-side-effects-on-delete-side-effect-handler.service.spec.ts b/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/object-metadata/services/__tests__/object-system-side-effects-on-delete-side-effect-handler.service.spec.ts index 655572f049..3bf2113e06 100644 --- a/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/object-metadata/services/__tests__/object-system-side-effects-on-delete-side-effect-handler.service.spec.ts +++ b/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/object-metadata/services/__tests__/object-system-side-effects-on-delete-side-effect-handler.service.spec.ts @@ -99,6 +99,74 @@ describe('ObjectSystemSideEffectsOnDeleteSideEffectHandlerService', () => { ).toEqual([SEARCH_FIELD_METADATA_UNIVERSAL_IDENTIFIER]); }); + it('should cascade-delete the default-relation reverse morph field on a standard object and its join-column index', () => { + const FORWARD_FIELD_UNIVERSAL_IDENTIFIER = + 'd1d2d3d4-d5d6-4000-8000-000000000003'; + const REVERSE_FIELD_UNIVERSAL_IDENTIFIER = + 'd1d2d3d4-d5d6-4000-8000-000000000004'; + const REVERSE_INDEX_UNIVERSAL_IDENTIFIER = + 'e1e2e3e4-e5e6-4000-8000-000000000002'; + + const result = handler.buildSideEffects( + buildArgs({ + relatedFlatEntityMaps: { + flatFieldMetadataMaps: { + byUniversalIdentifier: { + [FORWARD_FIELD_UNIVERSAL_IDENTIFIER]: { + universalIdentifier: FORWARD_FIELD_UNIVERSAL_IDENTIFIER, + isSystemSideEffect: true, + objectMetadataUniversalIdentifier: OBJECT_UNIVERSAL_IDENTIFIER, + relationTargetObjectMetadataUniversalIdentifier: + OTHER_OBJECT_UNIVERSAL_IDENTIFIER, + }, + [REVERSE_FIELD_UNIVERSAL_IDENTIFIER]: { + universalIdentifier: REVERSE_FIELD_UNIVERSAL_IDENTIFIER, + isSystemSideEffect: true, + objectMetadataUniversalIdentifier: + OTHER_OBJECT_UNIVERSAL_IDENTIFIER, + relationTargetObjectMetadataUniversalIdentifier: + OBJECT_UNIVERSAL_IDENTIFIER, + }, + }, + }, + flatIndexMaps: { + byUniversalIdentifier: { + [REVERSE_INDEX_UNIVERSAL_IDENTIFIER]: { + universalIdentifier: REVERSE_INDEX_UNIVERSAL_IDENTIFIER, + isSystemSideEffect: true, + objectMetadataUniversalIdentifier: + OTHER_OBJECT_UNIVERSAL_IDENTIFIER, + universalFlatIndexFieldMetadatas: [ + { + fieldMetadataUniversalIdentifier: + REVERSE_FIELD_UNIVERSAL_IDENTIFIER, + }, + ], + }, + }, + }, + flatSearchFieldMetadataMaps: { byUniversalIdentifier: {} }, + }, + }), + ); + + expect(result.status).toBe('success'); + + if (result.status !== 'success') { + throw new Error('expected success'); + } + + expect( + Object.keys(result.operations.fieldMetadata?.flatEntityToDelete ?? {}), + ).toEqual([ + FORWARD_FIELD_UNIVERSAL_IDENTIFIER, + REVERSE_FIELD_UNIVERSAL_IDENTIFIER, + ]); + expect( + Object.keys(result.operations.index?.flatEntityToDelete ?? {}), + ).toEqual([REVERSE_INDEX_UNIVERSAL_IDENTIFIER]); + }); + it('should not delete entities belonging to a different object', () => { const result = handler.buildSideEffects( buildArgs({ diff --git a/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/object-metadata/services/object-system-relations-on-create-side-effect-handler.service.ts b/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/object-metadata/services/object-system-relations-on-create-side-effect-handler.service.ts new file mode 100644 index 0000000000..a5b703f5be --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/object-metadata/services/object-system-relations-on-create-side-effect-handler.service.ts @@ -0,0 +1,117 @@ +import { msg, t } from '@lingui/core/macro'; +import { Injectable } from '@nestjs/common'; +import { isNonEmptyArray } from '@sniptt/guards'; + +import { + DEFAULT_RELATIONS_OBJECTS_STANDARD_IDS, + STANDARD_OBJECTS, +} from 'twenty-shared/metadata'; +import { fromArrayToUniqueKeyRecord, isDefined } from 'twenty-shared/utils'; + +import { type MetadataFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-flat-entity.type'; +import { MetadataSideEffectExceptionCode } from 'src/engine/metadata-modules/metadata-side-effect/exceptions/metadata-side-effect-exception-code'; +import { + type BuildSideEffectsArgs, + MetadataSideEffectHandler, +} from 'src/engine/metadata-modules/metadata-side-effect/interfaces/base-metadata-side-effect-handler.service'; +import { + type MetadataSideEffectFailure, + type MetadataSideEffectResult, +} from 'src/engine/metadata-modules/metadata-side-effect/types/metadata-side-effect-result.type'; +import { buildSystemRelationFlatFieldMetadatasForObject } from 'src/engine/metadata-modules/object-metadata/utils/build-system-relation-flat-field-metadatas-for-object.util'; +import { type UniversalFlatObjectMetadata } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-object-metadata.type'; + +type DefaultRelationStandardObjectNameSingular = + (typeof DEFAULT_RELATIONS_OBJECTS_STANDARD_IDS)[number]; + +@Injectable() +export class ObjectSystemRelationsOnCreateSideEffectHandlerService extends MetadataSideEffectHandler( + { + operation: 'create', + metadataName: 'objectMetadata', + name: 'objectSystemRelationsOnCreate', + description: + 'When an object is created, provision its default relations to the four standard relation objects (timelineActivity, attachment, noteTarget, taskTarget): the forward RELATION field on the new object, the reverse MORPH_RELATION field on the standard object (name-free deterministic identifier so an object rename is a lossless update), and the reverse join-column index. All emitted entities are isSystemSideEffect, so the engine owns their lifecycle. twenty-standard authors these fields itself and never reaches this handler (it syncs via the FromTo path). The handler always emits its bundles: a caller-provided field colliding on universal identifier hard-fails at merge time (RESERVED_SYSTEM_UNIVERSAL_IDENTIFIER), and a caller field colliding on name hard-fails in the field validator (NOT_AVAILABLE).', + }, +) { + buildSideEffects({ + flatEntity: flatObjectMetadata, + relatedFlatEntityMaps, + }: BuildSideEffectsArgs<'objectMetadata'>): MetadataSideEffectResult { + const sourceFlatObjectMetadata = + flatObjectMetadata as UniversalFlatObjectMetadata; + + const standardTargetFlatObjectMetadataByNameSingular = {} as Record< + DefaultRelationStandardObjectNameSingular, + UniversalFlatObjectMetadata + >; + + const missingStandardObjectErrors: MetadataSideEffectFailure['errors'] = []; + + for (const standardObjectNameSingular of DEFAULT_RELATIONS_OBJECTS_STANDARD_IDS) { + const standardTargetFlatObjectMetadata = + relatedFlatEntityMaps.flatObjectMetadataMaps.byUniversalIdentifier[ + STANDARD_OBJECTS[standardObjectNameSingular].universalIdentifier + ]; + + if (!isDefined(standardTargetFlatObjectMetadata)) { + missingStandardObjectErrors.push({ + code: MetadataSideEffectExceptionCode.SIDE_EFFECT_PARENT_METADATA_NOT_FOUND, + message: t`Could not resolve standard relation object "${standardObjectNameSingular}" to provision default relations`, + userFriendlyMessage: msg`A standard object required to provision default relations could not be found`, + }); + continue; + } + + standardTargetFlatObjectMetadataByNameSingular[ + standardObjectNameSingular + ] = standardTargetFlatObjectMetadata; + } + + if (isNonEmptyArray(missingStandardObjectErrors)) { + return { + status: 'fail', + type: 'create', + metadataName: 'objectMetadata', + flatEntityMinimalInformation: { + universalIdentifier: sourceFlatObjectMetadata.universalIdentifier, + nameSingular: sourceFlatObjectMetadata.nameSingular, + } as Partial>, + errors: missingStandardObjectErrors, + }; + } + + const systemRelationBundles = + buildSystemRelationFlatFieldMetadatasForObject({ + sourceFlatObjectMetadata, + standardTargetFlatObjectMetadataByNameSingular, + applicationUniversalIdentifier: + sourceFlatObjectMetadata.applicationUniversalIdentifier, + }); + + return { + status: 'success', + operations: { + fieldMetadata: { + flatEntityToCreate: fromArrayToUniqueKeyRecord({ + array: systemRelationBundles.flatMap( + ({ forwardFlatFieldMetadata, reverseFlatFieldMetadata }) => [ + forwardFlatFieldMetadata, + reverseFlatFieldMetadata, + ], + ), + uniqueKey: 'universalIdentifier', + }), + }, + index: { + flatEntityToCreate: fromArrayToUniqueKeyRecord({ + array: systemRelationBundles.map( + ({ flatIndexMetadata }) => flatIndexMetadata, + ), + uniqueKey: 'universalIdentifier', + }), + }, + }, + }; + } +} diff --git a/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/object-metadata/services/object-system-relations-on-update-side-effect-handler.service.ts b/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/object-metadata/services/object-system-relations-on-update-side-effect-handler.service.ts new file mode 100644 index 0000000000..82f2f3b39b --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/object-metadata/services/object-system-relations-on-update-side-effect-handler.service.ts @@ -0,0 +1,105 @@ +import { msg, t } from '@lingui/core/macro'; +import { Injectable } from '@nestjs/common'; + +import { fromArrayToUniqueKeyRecord, isDefined } from 'twenty-shared/utils'; + +import { type MetadataFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-flat-entity.type'; +import { renameRelatedMorphFieldOnObjectNamesUpdate } from 'src/engine/metadata-modules/flat-object-metadata/utils/rename-related-morph-field-on-object-names-update.util'; +import { MetadataSideEffectExceptionCode } from 'src/engine/metadata-modules/metadata-side-effect/exceptions/metadata-side-effect-exception-code'; +import { + type BuildSideEffectsArgs, + MetadataSideEffectHandler, +} from 'src/engine/metadata-modules/metadata-side-effect/interfaces/base-metadata-side-effect-handler.service'; +import { type MetadataSideEffectResult } from 'src/engine/metadata-modules/metadata-side-effect/types/metadata-side-effect-result.type'; +import { type UniversalFlatObjectMetadata } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-object-metadata.type'; + +@Injectable() +export class ObjectSystemRelationsOnUpdateSideEffectHandlerService extends MetadataSideEffectHandler( + { + operation: 'update', + metadataName: 'objectMetadata', + name: 'objectSystemRelationsOnUpdate', + description: + 'When an object is renamed, rename the reverse MORPH_RELATION fields of its default relations to the standard objects (timelineActivity, attachment, noteTarget, taskTarget) and recompute their join-column index names. These reverse fields are isSystemSideEffect, so the engine is their sole authority on rename across both the API and manifest-sync paths (the API transpiler renames only user-authored morph relations). The reverse field universal identifier is name-free, so a rename stays a lossless update. The computed updates are emitted unconditionally; any universal identifier collision with a caller-provided operation is arbitrated by the engine merge.', + }, +) { + buildSideEffects({ + flatEntity, + relatedFlatEntityMaps, + }: BuildSideEffectsArgs<'objectMetadata'>): MetadataSideEffectResult { + const updatedFlatObjectMetadata = flatEntity as UniversalFlatObjectMetadata; + + const existingFlatObjectMetadata = + relatedFlatEntityMaps.flatObjectMetadataMaps.byUniversalIdentifier[ + updatedFlatObjectMetadata.universalIdentifier + ]; + + if (!isDefined(existingFlatObjectMetadata)) { + return { + status: 'fail', + type: 'update', + metadataName: 'objectMetadata', + flatEntityMinimalInformation: { + universalIdentifier: updatedFlatObjectMetadata.universalIdentifier, + nameSingular: updatedFlatObjectMetadata.nameSingular, + } as Partial>, + errors: [ + { + code: MetadataSideEffectExceptionCode.SIDE_EFFECT_PARENT_METADATA_NOT_FOUND, + message: t`Could not resolve the existing object "${updatedFlatObjectMetadata.nameSingular}" to rename its default relations`, + userFriendlyMessage: msg`The object to rename could not be found to update its default relations`, + }, + ], + }; + } + + const isRenamed = + existingFlatObjectMetadata.nameSingular !== + updatedFlatObjectMetadata.nameSingular || + existingFlatObjectMetadata.namePlural !== + updatedFlatObjectMetadata.namePlural; + + if (!isRenamed) { + return { status: 'noop' }; + } + + const { morphFlatFieldMetadatasToUpdate, morphRelatedFlatIndexesToUpdate } = + renameRelatedMorphFieldOnObjectNamesUpdate({ + fromFlatObjectMetadata: existingFlatObjectMetadata, + toFlatObjectMetadata: { + ...existingFlatObjectMetadata, + nameSingular: updatedFlatObjectMetadata.nameSingular, + namePlural: updatedFlatObjectMetadata.namePlural, + }, + flatFieldMetadataMaps: relatedFlatEntityMaps.flatFieldMetadataMaps, + flatObjectMetadataMaps: relatedFlatEntityMaps.flatObjectMetadataMaps, + flatIndexMaps: relatedFlatEntityMaps.flatIndexMaps, + systemSideEffectMorphFieldsOnly: true, + }); + + if ( + morphFlatFieldMetadatasToUpdate.length === 0 && + morphRelatedFlatIndexesToUpdate.length === 0 + ) { + return { status: 'noop' }; + } + + return { + status: 'success', + operations: { + fieldMetadata: { + flatEntityToUpdate: fromArrayToUniqueKeyRecord({ + array: morphFlatFieldMetadatasToUpdate, + uniqueKey: 'universalIdentifier', + }), + }, + index: { + flatEntityToUpdate: fromArrayToUniqueKeyRecord({ + array: morphRelatedFlatIndexesToUpdate, + uniqueKey: 'universalIdentifier', + }), + }, + }, + }; + } +} diff --git a/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/object-metadata/services/object-system-side-effects-on-delete-side-effect-handler.service.ts b/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/object-metadata/services/object-system-side-effects-on-delete-side-effect-handler.service.ts index d79299a246..d04c27b89a 100644 --- a/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/object-metadata/services/object-system-side-effects-on-delete-side-effect-handler.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/metadata-side-effect/handlers/object-metadata/services/object-system-side-effects-on-delete-side-effect-handler.service.ts @@ -17,7 +17,7 @@ export class ObjectSystemSideEffectsOnDeleteSideEffectHandlerService extends Met metadataName: 'objectMetadata', name: 'objectSystemSideEffectsOnDelete', description: - 'When an object is deleted, cascade-delete its engine-owned side effects: the reserved system fields, every system index (including the GIN searchVector index), and its searchFieldMetadata rows. These entities are excluded from manifest deletion inference, so the cascade must be explicit. Caller-provided defaults (name, default relations) are NOT engine-owned and are deleted through normal deletion inference / the object delete transpiler.', + 'When an object is deleted, cascade-delete its engine-owned side effects: the reserved system fields, the default relation fields (forward field on the deleted object and reverse morph field on the standard object), every system index (reverse join-column indexes, the GIN searchVector index), and its searchFieldMetadata rows. The engine is the sole authority for isSystemSideEffect entities on delete: the API object delete transpiler cascades only user-authored fields and indexes, and manifest deletion inference excludes these entities entirely. Caller-provided defaults (e.g. the name field) are NOT engine-owned and are deleted through normal deletion inference / the object delete transpiler.', }, ) { buildSideEffects({ diff --git a/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.service.ts b/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.service.ts index 9a2ca3db4c..ada18e1047 100644 --- a/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.service.ts @@ -483,24 +483,11 @@ export class ObjectMetadataService extends TypeOrmQueryService; - -export type BuildDefaultRelationFieldsForCustomObjectArgs = { - existingFlatObjectMetadataMaps: FlatEntityMaps; - sourceFlatObjectMetadata: UniversalFlatObjectMetadata; - flatApplication: FlatApplication; -}; - -type SourceAndTargetFlatFieldMetadatasRecord = { - standardSourceFlatFieldMetadatas: UniversalFlatFieldMetadata[]; - standardTargetFlatFieldMetadatas: UniversalFlatFieldMetadata[]; - standardTargetFlatIndexMetadatas: UniversalFlatIndexMetadata[]; -}; -const EMPTY_SOURCE_AND_TARGET_FLAT_FIELD_METADATAS_RECORD: SourceAndTargetFlatFieldMetadatasRecord = - { - standardSourceFlatFieldMetadatas: [], - standardTargetFlatFieldMetadatas: [], - standardTargetFlatIndexMetadatas: [], - }; - -export const buildDefaultRelationFlatFieldMetadatasForCustomObject = ({ - existingFlatObjectMetadataMaps, - sourceFlatObjectMetadata, - flatApplication, -}: BuildDefaultRelationFieldsForCustomObjectArgs): SourceAndTargetFlatFieldMetadatasRecord => { - const objectIdByNameSingular = Object.values( - existingFlatObjectMetadataMaps.byUniversalIdentifier, - ).reduce>((acc, flatObject) => { - if (!isDefined(flatObject)) { - return acc; - } - - return { - ...acc, - [flatObject.nameSingular]: flatObject.id, - }; - }, {}); - - const result = - DEFAULT_RELATIONS_OBJECTS_STANDARD_IDS.reduce( - (sourceAndTargetFlatFieldMetadatasRecord, objectMetadataNameSingular) => { - const isObjectMigratedToMorphRelations = - objectMetadataNameSingular === 'timelineActivity' || - objectMetadataNameSingular === 'attachment' || - objectMetadataNameSingular === 'noteTarget' || - objectMetadataNameSingular === 'taskTarget'; - - const targetFlatObjectMetadataId = - objectIdByNameSingular[objectMetadataNameSingular]; - - if (!isDefined(targetFlatObjectMetadataId)) { - throw new ObjectMetadataException( - `Standard target object metadata id ${targetFlatObjectMetadataId} not found in cache`, - ObjectMetadataExceptionCode.INTERNAL_SERVER_ERROR, - ); - } - - const targetFlatObjectMetadata = - findFlatEntityByIdInFlatEntityMapsOrThrow({ - flatEntityMaps: existingFlatObjectMetadataMaps, - flatEntityId: targetFlatObjectMetadataId, - }); - - const targetFieldIcon = - STANDARD_OBJECT_ICONS[ - targetFlatObjectMetadata.nameSingular as keyof typeof STANDARD_OBJECT_ICONS - ] || 'IconBuildingSkyscraper'; - - const standardFieldProperties = - STANDARD_RELATION_FIELD_PROPERTIES_BY_RELATION_OBJECT[ - objectMetadataNameSingular - ]; - - const morphFieldName = `target${capitalize(sourceFlatObjectMetadata.nameSingular)}`; - const fieldName = isObjectMigratedToMorphRelations - ? morphFieldName - : sourceFlatObjectMetadata.nameSingular; - const joinColumnName = computeMorphOrRelationFieldJoinColumnName({ - name: fieldName, - }); - - const morphId = - morphIdByRelationObjectNameSingular[objectMetadataNameSingular]; - - // Default relation fields get deterministic universal identifiers so - // that provisioning the same custom object always yields the same - // identifiers (matching the SDK manifest derivation). - const sourceFieldUniversalIdentifier = getFieldUniversalIdentifier({ - applicationUniversalIdentifier: flatApplication.universalIdentifier, - objectUniversalIdentifier: - sourceFlatObjectMetadata.universalIdentifier, - name: targetFlatObjectMetadata.namePlural, - }); - const targetFieldUniversalIdentifier = getFieldUniversalIdentifier({ - applicationUniversalIdentifier: flatApplication.universalIdentifier, - objectUniversalIdentifier: - targetFlatObjectMetadata.universalIdentifier, - name: fieldName, - }); - - const { flatFieldMetadatas, indexMetadatas } = - generateMorphOrRelationFlatFieldMetadataPair({ - sourceFlatObjectMetadata, - targetFlatObjectMetadata, - targetFlatFieldMetadataType: isObjectMigratedToMorphRelations - ? FieldMetadataType.MORPH_RELATION - : FieldMetadataType.RELATION, - flatApplication, - sourceFlatObjectMetadataJoinColumnName: joinColumnName, - morphId, - targetFieldName: fieldName, - sourceFieldUniversalIdentifier, - targetFieldUniversalIdentifier, - createFieldInput: { - icon: standardFieldProperties.icon, - type: FieldMetadataType.RELATION, - name: targetFlatObjectMetadata.namePlural, - label: i18nLabel(standardFieldProperties.label), - isSystem: false, - relationCreationPayload: { - type: RelationType.ONE_TO_MANY, - targetObjectMetadataId: targetFlatObjectMetadata.id, - targetFieldLabel: capitalize( - sourceFlatObjectMetadata.nameSingular, - ), - targetFieldIcon: targetFieldIcon, - }, - }, - }); - - return { - standardSourceFlatFieldMetadatas: [ - ...sourceAndTargetFlatFieldMetadatasRecord.standardSourceFlatFieldMetadatas, - flatFieldMetadatas[0], - ], - standardTargetFlatFieldMetadatas: [ - ...sourceAndTargetFlatFieldMetadatasRecord.standardTargetFlatFieldMetadatas, - flatFieldMetadatas[1], - ], - standardTargetFlatIndexMetadatas: [ - ...sourceAndTargetFlatFieldMetadatasRecord.standardTargetFlatIndexMetadatas, - ...indexMetadatas, - ], - }; - }, - EMPTY_SOURCE_AND_TARGET_FLAT_FIELD_METADATAS_RECORD, - ); - - return result; -}; diff --git a/packages/twenty-server/src/engine/metadata-modules/object-metadata/utils/build-system-relation-flat-field-metadatas-for-object.util.ts b/packages/twenty-server/src/engine/metadata-modules/object-metadata/utils/build-system-relation-flat-field-metadatas-for-object.util.ts new file mode 100644 index 0000000000..5d625f6b88 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/object-metadata/utils/build-system-relation-flat-field-metadatas-for-object.util.ts @@ -0,0 +1,124 @@ +import { getSystemRelationFieldUniversalIdentifier } from 'twenty-shared/application'; +import { + DEFAULT_RELATIONS_OBJECTS_STANDARD_IDS, + STANDARD_OBJECTS, +} from 'twenty-shared/metadata'; +import { FieldMetadataType } from 'twenty-shared/types'; +import { capitalize } from 'twenty-shared/utils'; + +import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface'; +import { computeMorphOrRelationFieldJoinColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-morph-or-relation-field-join-column-name.util'; +import { generateMorphOrRelationFlatFieldMetadataPair } from 'src/engine/metadata-modules/flat-field-metadata/utils/generate-morph-or-relation-flat-field-metadata-pair.util'; +import { STANDARD_RELATION_FIELD_PROPERTIES_BY_RELATION_OBJECT } from 'src/engine/metadata-modules/object-metadata/constants/standard-relation-field-properties.constant'; +import { i18nLabel } from 'src/engine/workspace-manager/twenty-standard-application/utils/i18n-label.util'; +import { STANDARD_OBJECT_ICONS } from 'src/engine/workspace-manager/workspace-migration/constant/standard-object-icons'; +import { type UniversalFlatFieldMetadata } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-field-metadata.type'; +import { type UniversalFlatIndexMetadata } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-index-metadata.type'; +import { type UniversalFlatObjectMetadata } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-object-metadata.type'; + +type DefaultRelationStandardObjectNameSingular = + (typeof DEFAULT_RELATIONS_OBJECTS_STANDARD_IDS)[number]; + +const MORPH_ID_BY_STANDARD_OBJECT_NAME_SINGULAR = { + timelineActivity: + STANDARD_OBJECTS.timelineActivity.morphIds.targetMorphId.morphId, + attachment: STANDARD_OBJECTS.attachment.morphIds.targetMorphId.morphId, + noteTarget: STANDARD_OBJECTS.noteTarget.morphIds.targetMorphId.morphId, + taskTarget: STANDARD_OBJECTS.taskTarget.morphIds.targetMorphId.morphId, +} satisfies Record; + +export type SystemRelationFlatFieldMetadataBundle = { + forwardFlatFieldMetadata: UniversalFlatFieldMetadata; + reverseFlatFieldMetadata: UniversalFlatFieldMetadata; + flatIndexMetadata: UniversalFlatIndexMetadata; +}; + +type BuildSystemRelationFlatFieldMetadatasForObjectArgs = { + sourceFlatObjectMetadata: UniversalFlatObjectMetadata; + standardTargetFlatObjectMetadataByNameSingular: Record< + DefaultRelationStandardObjectNameSingular, + UniversalFlatObjectMetadata + >; + applicationUniversalIdentifier: string; +}; + +export const buildSystemRelationFlatFieldMetadatasForObject = ({ + sourceFlatObjectMetadata, + standardTargetFlatObjectMetadataByNameSingular, + applicationUniversalIdentifier, +}: BuildSystemRelationFlatFieldMetadatasForObjectArgs): SystemRelationFlatFieldMetadataBundle[] => + DEFAULT_RELATIONS_OBJECTS_STANDARD_IDS.map((standardObjectNameSingular) => { + const targetFlatObjectMetadata = + standardTargetFlatObjectMetadataByNameSingular[ + standardObjectNameSingular + ]; + + const reverseFieldName = `target${capitalize( + sourceFlatObjectMetadata.nameSingular, + )}`; + const joinColumnName = computeMorphOrRelationFieldJoinColumnName({ + name: reverseFieldName, + }); + const morphId = + MORPH_ID_BY_STANDARD_OBJECT_NAME_SINGULAR[standardObjectNameSingular]; + + const standardFieldProperties = + STANDARD_RELATION_FIELD_PROPERTIES_BY_RELATION_OBJECT[ + standardObjectNameSingular + ]; + const reverseFieldIcon = + STANDARD_OBJECT_ICONS[ + targetFlatObjectMetadata.nameSingular as keyof typeof STANDARD_OBJECT_ICONS + ] ?? 'IconBuildingSkyscraper'; + + const forwardFieldUniversalIdentifier = + getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier, + objectUniversalIdentifier: sourceFlatObjectMetadata.universalIdentifier, + relationTargetObjectUniversalIdentifier: + targetFlatObjectMetadata.universalIdentifier, + }); + const reverseFieldUniversalIdentifier = + getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier, + objectUniversalIdentifier: targetFlatObjectMetadata.universalIdentifier, + relationTargetObjectUniversalIdentifier: + sourceFlatObjectMetadata.universalIdentifier, + }); + + const { flatFieldMetadatas, indexMetadatas } = + generateMorphOrRelationFlatFieldMetadataPair({ + sourceFlatObjectMetadata, + targetFlatObjectMetadata, + targetFlatFieldMetadataType: FieldMetadataType.MORPH_RELATION, + applicationUniversalIdentifier, + sourceFlatObjectMetadataJoinColumnName: joinColumnName, + morphId, + targetFieldName: reverseFieldName, + sourceFieldUniversalIdentifier: forwardFieldUniversalIdentifier, + targetFieldUniversalIdentifier: reverseFieldUniversalIdentifier, + isSystemSideEffect: true, + createFieldInput: { + icon: standardFieldProperties.icon, + type: FieldMetadataType.RELATION, + name: targetFlatObjectMetadata.namePlural, + label: i18nLabel(standardFieldProperties.label), + relationCreationPayload: { + targetObjectMetadataId: + targetFlatObjectMetadata.universalIdentifier, + type: RelationType.ONE_TO_MANY, + targetFieldLabel: capitalize(sourceFlatObjectMetadata.nameSingular), + targetFieldIcon: reverseFieldIcon, + }, + }, + }); + + const [forwardFlatFieldMetadata, reverseFlatFieldMetadata] = + flatFieldMetadatas; + + return { + forwardFlatFieldMetadata, + reverseFlatFieldMetadata, + flatIndexMetadata: indexMetadatas[0], + }; + }); diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-attachment-standard-flat-field-metadata.util.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-attachment-standard-flat-field-metadata.util.ts index 0dcf590095..fbfdcf7531 100644 --- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-attachment-standard-flat-field-metadata.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-attachment-standard-flat-field-metadata.util.ts @@ -339,11 +339,12 @@ export const buildAttachmentStandardFlatFieldMetadatas = ({ type: FieldMetadataType.MORPH_RELATION, morphId: STANDARD_OBJECTS.attachment.morphIds.targetMorphId.morphId, fieldName: 'targetTask', - label: i18nLabel(msg`Target`), + label: i18nLabel(msg`Task`), description: i18nLabel(msg`Attachment target`), - icon: 'IconArrowUpRight', + icon: 'IconFileImport', isNullable: true, isUIEditable: false, + isSystemSideEffect: true, targetObjectName: 'task', targetFieldName: 'attachments', settings: { @@ -364,11 +365,12 @@ export const buildAttachmentStandardFlatFieldMetadatas = ({ type: FieldMetadataType.MORPH_RELATION, morphId: STANDARD_OBJECTS.attachment.morphIds.targetMorphId.morphId, fieldName: 'targetNote', - label: i18nLabel(msg`Target`), + label: i18nLabel(msg`Note`), description: i18nLabel(msg`Attachment target`), - icon: 'IconArrowUpRight', + icon: 'IconFileImport', isNullable: true, isUIEditable: false, + isSystemSideEffect: true, targetObjectName: 'note', targetFieldName: 'attachments', settings: { @@ -389,11 +391,12 @@ export const buildAttachmentStandardFlatFieldMetadatas = ({ type: FieldMetadataType.MORPH_RELATION, morphId: STANDARD_OBJECTS.attachment.morphIds.targetMorphId.morphId, fieldName: 'targetPerson', - label: i18nLabel(msg`Target`), + label: i18nLabel(msg`Person`), description: i18nLabel(msg`Attachment target`), - icon: 'IconArrowUpRight', + icon: 'IconFileImport', isNullable: true, isUIEditable: false, + isSystemSideEffect: true, targetObjectName: 'person', targetFieldName: 'attachments', settings: { @@ -414,11 +417,12 @@ export const buildAttachmentStandardFlatFieldMetadatas = ({ type: FieldMetadataType.MORPH_RELATION, morphId: STANDARD_OBJECTS.attachment.morphIds.targetMorphId.morphId, fieldName: 'targetCompany', - label: i18nLabel(msg`Target`), + label: i18nLabel(msg`Company`), description: i18nLabel(msg`Attachment target`), - icon: 'IconArrowUpRight', + icon: 'IconFileImport', isNullable: true, isUIEditable: false, + isSystemSideEffect: true, targetObjectName: 'company', targetFieldName: 'attachments', settings: { @@ -439,11 +443,12 @@ export const buildAttachmentStandardFlatFieldMetadatas = ({ type: FieldMetadataType.MORPH_RELATION, morphId: STANDARD_OBJECTS.attachment.morphIds.targetMorphId.morphId, fieldName: 'targetOpportunity', - label: i18nLabel(msg`Target`), + label: i18nLabel(msg`Opportunity`), description: i18nLabel(msg`Attachment target`), - icon: 'IconArrowUpRight', + icon: 'IconFileImport', isNullable: true, isUIEditable: false, + isSystemSideEffect: true, targetObjectName: 'opportunity', targetFieldName: 'attachments', settings: { @@ -464,11 +469,12 @@ export const buildAttachmentStandardFlatFieldMetadatas = ({ type: FieldMetadataType.MORPH_RELATION, morphId: STANDARD_OBJECTS.attachment.morphIds.targetMorphId.morphId, fieldName: 'targetDashboard', - label: i18nLabel(msg`Target`), + label: i18nLabel(msg`Dashboard`), description: i18nLabel(msg`Attachment target`), - icon: 'IconArrowUpRight', + icon: 'IconFileImport', isNullable: true, isUIEditable: false, + isSystemSideEffect: true, targetObjectName: 'dashboard', targetFieldName: 'attachments', settings: { @@ -489,11 +495,12 @@ export const buildAttachmentStandardFlatFieldMetadatas = ({ type: FieldMetadataType.MORPH_RELATION, morphId: STANDARD_OBJECTS.attachment.morphIds.targetMorphId.morphId, fieldName: 'targetWorkflow', - label: i18nLabel(msg`Target`), + label: i18nLabel(msg`Workflow`), description: i18nLabel(msg`Attachment target`), - icon: 'IconArrowUpRight', + icon: 'IconFileImport', isNullable: true, isUIEditable: false, + isSystemSideEffect: true, targetObjectName: 'workflow', targetFieldName: 'attachments', settings: { diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-company-standard-flat-field-metadata.util.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-company-standard-flat-field-metadata.util.ts index a620adc168..1c6ffed506 100644 --- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-company-standard-flat-field-metadata.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-company-standard-flat-field-metadata.util.ts @@ -339,6 +339,7 @@ export const buildCompanyStandardFlatFieldMetadatas = ({ type: FieldMetadataType.RELATION, morphId: null, fieldName: 'taskTargets', + isSystemSideEffect: true, label: i18nLabel( STANDARD_RELATION_FIELD_PROPERTIES_BY_RELATION_OBJECT.taskTarget.label, ), @@ -365,6 +366,7 @@ export const buildCompanyStandardFlatFieldMetadatas = ({ type: FieldMetadataType.RELATION, morphId: null, fieldName: 'noteTargets', + isSystemSideEffect: true, label: i18nLabel( STANDARD_RELATION_FIELD_PROPERTIES_BY_RELATION_OBJECT.noteTarget.label, ), @@ -413,6 +415,7 @@ export const buildCompanyStandardFlatFieldMetadatas = ({ type: FieldMetadataType.RELATION, morphId: null, fieldName: 'attachments', + isSystemSideEffect: true, label: i18nLabel( STANDARD_RELATION_FIELD_PROPERTIES_BY_RELATION_OBJECT.attachment.label, ), @@ -438,6 +441,7 @@ export const buildCompanyStandardFlatFieldMetadatas = ({ type: FieldMetadataType.RELATION, morphId: null, fieldName: 'timelineActivities', + isSystemSideEffect: true, label: i18nLabel( STANDARD_RELATION_FIELD_PROPERTIES_BY_RELATION_OBJECT.timelineActivity .label, diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-dashboard-standard-flat-field-metadata.util.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-dashboard-standard-flat-field-metadata.util.ts index f39f096c6f..e9aa140348 100644 --- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-dashboard-standard-flat-field-metadata.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-dashboard-standard-flat-field-metadata.util.ts @@ -231,6 +231,7 @@ export const buildDashboardStandardFlatFieldMetadatas = ({ type: FieldMetadataType.RELATION, morphId: null, fieldName: 'timelineActivities', + isSystemSideEffect: true, label: i18nLabel( STANDARD_RELATION_FIELD_PROPERTIES_BY_RELATION_OBJECT.timelineActivity .label, @@ -257,6 +258,7 @@ export const buildDashboardStandardFlatFieldMetadatas = ({ type: FieldMetadataType.RELATION, morphId: null, fieldName: 'attachments', + isSystemSideEffect: true, label: i18nLabel( STANDARD_RELATION_FIELD_PROPERTIES_BY_RELATION_OBJECT.attachment.label, ), diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-message-campaign-standard-flat-field-metadata.util.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-message-campaign-standard-flat-field-metadata.util.ts index 075bb089d3..390ec22226 100644 --- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-message-campaign-standard-flat-field-metadata.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-message-campaign-standard-flat-field-metadata.util.ts @@ -353,6 +353,7 @@ export const buildMessageCampaignStandardFlatFieldMetadatas = ({ type: FieldMetadataType.RELATION, morphId: null, fieldName: 'timelineActivities', + isSystemSideEffect: true, label: i18nLabel(msg`Events`), description: i18nLabel(msg`Events linked to the campaign`), icon: 'IconTimelineEvent', diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-message-list-standard-flat-field-metadata.util.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-message-list-standard-flat-field-metadata.util.ts index 4171fcaa9a..408cb2d80a 100644 --- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-message-list-standard-flat-field-metadata.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-message-list-standard-flat-field-metadata.util.ts @@ -240,6 +240,7 @@ export const buildMessageListStandardFlatFieldMetadatas = ({ type: FieldMetadataType.RELATION, morphId: null, fieldName: 'timelineActivities', + isSystemSideEffect: true, label: i18nLabel(msg`Events`), description: i18nLabel(msg`Events linked to the list`), icon: 'IconTimelineEvent', diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-note-standard-flat-field-metadata.util.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-note-standard-flat-field-metadata.util.ts index 37ada61d33..7d0560eb29 100644 --- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-note-standard-flat-field-metadata.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-note-standard-flat-field-metadata.util.ts @@ -258,6 +258,7 @@ export const buildNoteStandardFlatFieldMetadatas = ({ type: FieldMetadataType.RELATION, morphId: null, fieldName: 'attachments', + isSystemSideEffect: true, label: i18nLabel( STANDARD_RELATION_FIELD_PROPERTIES_BY_RELATION_OBJECT.attachment.label, ), @@ -283,6 +284,7 @@ export const buildNoteStandardFlatFieldMetadatas = ({ type: FieldMetadataType.RELATION, morphId: null, fieldName: 'timelineActivities', + isSystemSideEffect: true, label: i18nLabel( STANDARD_RELATION_FIELD_PROPERTIES_BY_RELATION_OBJECT.timelineActivity .label, diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-note-target-standard-flat-field-metadata.util.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-note-target-standard-flat-field-metadata.util.ts index 316d388ed7..4dd9d1e3a1 100644 --- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-note-target-standard-flat-field-metadata.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-note-target-standard-flat-field-metadata.util.ts @@ -228,11 +228,12 @@ export const buildNoteTargetStandardFlatFieldMetadatas = ({ type: FieldMetadataType.MORPH_RELATION, morphId: STANDARD_OBJECTS.noteTarget.morphIds.targetMorphId.morphId, fieldName: 'targetPerson', - label: i18nLabel(msg`Target`), + label: i18nLabel(msg`Person`), description: i18nLabel(msg`NoteTarget target`), - icon: 'IconArrowUpRight', + icon: 'IconCheckbox', isNullable: true, isUIEditable: false, + isSystemSideEffect: true, targetObjectName: 'person', targetFieldName: 'noteTargets', settings: { @@ -253,11 +254,12 @@ export const buildNoteTargetStandardFlatFieldMetadatas = ({ type: FieldMetadataType.MORPH_RELATION, morphId: STANDARD_OBJECTS.noteTarget.morphIds.targetMorphId.morphId, fieldName: 'targetCompany', - label: i18nLabel(msg`Target`), + label: i18nLabel(msg`Company`), description: i18nLabel(msg`NoteTarget target`), - icon: 'IconArrowUpRight', + icon: 'IconCheckbox', isNullable: true, isUIEditable: false, + isSystemSideEffect: true, targetObjectName: 'company', targetFieldName: 'noteTargets', settings: { @@ -278,11 +280,12 @@ export const buildNoteTargetStandardFlatFieldMetadatas = ({ type: FieldMetadataType.MORPH_RELATION, morphId: STANDARD_OBJECTS.noteTarget.morphIds.targetMorphId.morphId, fieldName: 'targetOpportunity', - label: i18nLabel(msg`Target`), + label: i18nLabel(msg`Opportunity`), description: i18nLabel(msg`NoteTarget target`), - icon: 'IconArrowUpRight', + icon: 'IconCheckbox', isNullable: true, isUIEditable: false, + isSystemSideEffect: true, targetObjectName: 'opportunity', targetFieldName: 'noteTargets', settings: { diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-opportunity-standard-flat-field-metadata.util.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-opportunity-standard-flat-field-metadata.util.ts index 4e1a62a919..f42e124c6b 100644 --- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-opportunity-standard-flat-field-metadata.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-opportunity-standard-flat-field-metadata.util.ts @@ -355,6 +355,7 @@ export const buildOpportunityStandardFlatFieldMetadatas = ({ type: FieldMetadataType.RELATION, morphId: null, fieldName: 'taskTargets', + isSystemSideEffect: true, label: i18nLabel( STANDARD_RELATION_FIELD_PROPERTIES_BY_RELATION_OBJECT.taskTarget.label, ), @@ -381,6 +382,7 @@ export const buildOpportunityStandardFlatFieldMetadatas = ({ type: FieldMetadataType.RELATION, morphId: null, fieldName: 'noteTargets', + isSystemSideEffect: true, label: i18nLabel( STANDARD_RELATION_FIELD_PROPERTIES_BY_RELATION_OBJECT.noteTarget.label, ), @@ -407,6 +409,7 @@ export const buildOpportunityStandardFlatFieldMetadatas = ({ type: FieldMetadataType.RELATION, morphId: null, fieldName: 'attachments', + isSystemSideEffect: true, label: i18nLabel( STANDARD_RELATION_FIELD_PROPERTIES_BY_RELATION_OBJECT.attachment.label, ), @@ -432,6 +435,7 @@ export const buildOpportunityStandardFlatFieldMetadatas = ({ type: FieldMetadataType.RELATION, morphId: null, fieldName: 'timelineActivities', + isSystemSideEffect: true, label: i18nLabel( STANDARD_RELATION_FIELD_PROPERTIES_BY_RELATION_OBJECT.timelineActivity .label, diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-person-standard-flat-field-metadata.util.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-person-standard-flat-field-metadata.util.ts index 8e3773da7a..66e84915c4 100644 --- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-person-standard-flat-field-metadata.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-person-standard-flat-field-metadata.util.ts @@ -363,6 +363,7 @@ export const buildPersonStandardFlatFieldMetadatas = ({ type: FieldMetadataType.RELATION, morphId: null, fieldName: 'taskTargets', + isSystemSideEffect: true, label: i18nLabel( STANDARD_RELATION_FIELD_PROPERTIES_BY_RELATION_OBJECT.taskTarget.label, ), @@ -389,6 +390,7 @@ export const buildPersonStandardFlatFieldMetadatas = ({ type: FieldMetadataType.RELATION, morphId: null, fieldName: 'noteTargets', + isSystemSideEffect: true, label: i18nLabel( STANDARD_RELATION_FIELD_PROPERTIES_BY_RELATION_OBJECT.noteTarget.label, ), @@ -415,6 +417,7 @@ export const buildPersonStandardFlatFieldMetadatas = ({ type: FieldMetadataType.RELATION, morphId: null, fieldName: 'attachments', + isSystemSideEffect: true, label: i18nLabel( STANDARD_RELATION_FIELD_PROPERTIES_BY_RELATION_OBJECT.attachment.label, ), @@ -484,6 +487,7 @@ export const buildPersonStandardFlatFieldMetadatas = ({ type: FieldMetadataType.RELATION, morphId: null, fieldName: 'timelineActivities', + isSystemSideEffect: true, label: i18nLabel(msg`Events`), description: i18nLabel(msg`Events linked to the person`), icon: 'IconTimelineEvent', diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-task-standard-flat-field-metadata.util.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-task-standard-flat-field-metadata.util.ts index 98b46ebf2a..b94b7662f0 100644 --- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-task-standard-flat-field-metadata.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-task-standard-flat-field-metadata.util.ts @@ -315,6 +315,7 @@ export const buildTaskStandardFlatFieldMetadatas = ({ type: FieldMetadataType.RELATION, morphId: null, fieldName: 'attachments', + isSystemSideEffect: true, label: i18nLabel( STANDARD_RELATION_FIELD_PROPERTIES_BY_RELATION_OBJECT.attachment.label, ), @@ -364,6 +365,7 @@ export const buildTaskStandardFlatFieldMetadatas = ({ type: FieldMetadataType.RELATION, morphId: null, fieldName: 'timelineActivities', + isSystemSideEffect: true, label: i18nLabel( STANDARD_RELATION_FIELD_PROPERTIES_BY_RELATION_OBJECT.timelineActivity .label, diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-task-target-standard-flat-field-metadata.util.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-task-target-standard-flat-field-metadata.util.ts index 40abc8a77d..77d82d9cf1 100644 --- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-task-target-standard-flat-field-metadata.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-task-target-standard-flat-field-metadata.util.ts @@ -228,11 +228,12 @@ export const buildTaskTargetStandardFlatFieldMetadatas = ({ type: FieldMetadataType.MORPH_RELATION, morphId: STANDARD_OBJECTS.taskTarget.morphIds.targetMorphId.morphId, fieldName: 'targetPerson', - label: i18nLabel(msg`Target`), + label: i18nLabel(msg`Person`), description: i18nLabel(msg`TaskTarget target`), - icon: 'IconArrowUpRight', + icon: 'IconCheckbox', isNullable: true, isUIEditable: false, + isSystemSideEffect: true, targetObjectName: 'person', targetFieldName: 'taskTargets', settings: { @@ -253,11 +254,12 @@ export const buildTaskTargetStandardFlatFieldMetadatas = ({ type: FieldMetadataType.MORPH_RELATION, morphId: STANDARD_OBJECTS.taskTarget.morphIds.targetMorphId.morphId, fieldName: 'targetCompany', - label: i18nLabel(msg`Target`), + label: i18nLabel(msg`Company`), description: i18nLabel(msg`TaskTarget target`), - icon: 'IconArrowUpRight', + icon: 'IconCheckbox', isNullable: true, isUIEditable: false, + isSystemSideEffect: true, targetObjectName: 'company', targetFieldName: 'taskTargets', settings: { @@ -278,11 +280,12 @@ export const buildTaskTargetStandardFlatFieldMetadatas = ({ type: FieldMetadataType.MORPH_RELATION, morphId: STANDARD_OBJECTS.taskTarget.morphIds.targetMorphId.morphId, fieldName: 'targetOpportunity', - label: i18nLabel(msg`Target`), + label: i18nLabel(msg`Opportunity`), description: i18nLabel(msg`TaskTarget target`), - icon: 'IconArrowUpRight', + icon: 'IconCheckbox', isNullable: true, isUIEditable: false, + isSystemSideEffect: true, targetObjectName: 'opportunity', targetFieldName: 'taskTargets', settings: { diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-timeline-activity-standard-flat-field-metadata.util.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-timeline-activity-standard-flat-field-metadata.util.ts index c5b20289d1..c3bcc1267a 100644 --- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-timeline-activity-standard-flat-field-metadata.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-timeline-activity-standard-flat-field-metadata.util.ts @@ -315,11 +315,12 @@ export const buildTimelineActivityStandardFlatFieldMetadatas = ({ type: FieldMetadataType.MORPH_RELATION, morphId: STANDARD_OBJECTS.timelineActivity.morphIds.targetMorphId.morphId, fieldName: 'targetPerson', - label: i18nLabel(msg`Target`), + label: i18nLabel(msg`Person`), description: i18nLabel(msg`Event target`), - icon: 'IconArrowUpRight', + icon: 'IconTimelineEvent', isNullable: true, isUIEditable: false, + isSystemSideEffect: true, targetObjectName: 'person', targetFieldName: 'timelineActivities', settings: { @@ -340,11 +341,12 @@ export const buildTimelineActivityStandardFlatFieldMetadatas = ({ type: FieldMetadataType.MORPH_RELATION, morphId: STANDARD_OBJECTS.timelineActivity.morphIds.targetMorphId.morphId, fieldName: 'targetCompany', - label: i18nLabel(msg`Target`), + label: i18nLabel(msg`Company`), description: i18nLabel(msg`Event target`), - icon: 'IconArrowUpRight', + icon: 'IconTimelineEvent', isNullable: true, isUIEditable: false, + isSystemSideEffect: true, targetObjectName: 'company', targetFieldName: 'timelineActivities', settings: { @@ -365,11 +367,12 @@ export const buildTimelineActivityStandardFlatFieldMetadatas = ({ type: FieldMetadataType.MORPH_RELATION, morphId: STANDARD_OBJECTS.timelineActivity.morphIds.targetMorphId.morphId, fieldName: 'targetOpportunity', - label: i18nLabel(msg`Target`), + label: i18nLabel(msg`Opportunity`), description: i18nLabel(msg`Event target`), - icon: 'IconArrowUpRight', + icon: 'IconTimelineEvent', isNullable: true, isUIEditable: false, + isSystemSideEffect: true, targetObjectName: 'opportunity', targetFieldName: 'timelineActivities', settings: { @@ -390,11 +393,12 @@ export const buildTimelineActivityStandardFlatFieldMetadatas = ({ type: FieldMetadataType.MORPH_RELATION, morphId: STANDARD_OBJECTS.timelineActivity.morphIds.targetMorphId.morphId, fieldName: 'targetNote', - label: i18nLabel(msg`Target`), + label: i18nLabel(msg`Note`), description: i18nLabel(msg`Event target`), - icon: 'IconArrowUpRight', + icon: 'IconTimelineEvent', isNullable: true, isUIEditable: false, + isSystemSideEffect: true, targetObjectName: 'note', targetFieldName: 'timelineActivities', settings: { @@ -415,11 +419,12 @@ export const buildTimelineActivityStandardFlatFieldMetadatas = ({ type: FieldMetadataType.MORPH_RELATION, morphId: STANDARD_OBJECTS.timelineActivity.morphIds.targetMorphId.morphId, fieldName: 'targetTask', - label: i18nLabel(msg`Target`), + label: i18nLabel(msg`Task`), description: i18nLabel(msg`Event target`), - icon: 'IconArrowUpRight', + icon: 'IconTimelineEvent', isNullable: true, isUIEditable: false, + isSystemSideEffect: true, targetObjectName: 'task', targetFieldName: 'timelineActivities', settings: { @@ -440,11 +445,12 @@ export const buildTimelineActivityStandardFlatFieldMetadatas = ({ type: FieldMetadataType.MORPH_RELATION, morphId: STANDARD_OBJECTS.timelineActivity.morphIds.targetMorphId.morphId, fieldName: 'targetWorkflow', - label: i18nLabel(msg`Target`), + label: i18nLabel(msg`Workflow`), description: i18nLabel(msg`Event target`), - icon: 'IconArrowUpRight', + icon: 'IconTimelineEvent', isNullable: true, isUIEditable: false, + isSystemSideEffect: true, targetObjectName: 'workflow', targetFieldName: 'timelineActivities', settings: { @@ -465,11 +471,12 @@ export const buildTimelineActivityStandardFlatFieldMetadatas = ({ type: FieldMetadataType.MORPH_RELATION, morphId: STANDARD_OBJECTS.timelineActivity.morphIds.targetMorphId.morphId, fieldName: 'targetWorkflowVersion', - label: i18nLabel(msg`Target`), + label: i18nLabel(msg`WorkflowVersion`), description: i18nLabel(msg`Event target`), - icon: 'IconArrowUpRight', + icon: 'IconTimelineEvent', isNullable: true, isUIEditable: false, + isSystemSideEffect: true, targetObjectName: 'workflowVersion', targetFieldName: 'timelineActivities', settings: { @@ -490,11 +497,12 @@ export const buildTimelineActivityStandardFlatFieldMetadatas = ({ type: FieldMetadataType.MORPH_RELATION, morphId: STANDARD_OBJECTS.timelineActivity.morphIds.targetMorphId.morphId, fieldName: 'targetWorkflowRun', - label: i18nLabel(msg`Target`), + label: i18nLabel(msg`WorkflowRun`), description: i18nLabel(msg`Event target`), - icon: 'IconArrowUpRight', + icon: 'IconTimelineEvent', isNullable: true, isUIEditable: false, + isSystemSideEffect: true, targetObjectName: 'workflowRun', targetFieldName: 'timelineActivities', settings: { @@ -515,11 +523,12 @@ export const buildTimelineActivityStandardFlatFieldMetadatas = ({ type: FieldMetadataType.MORPH_RELATION, morphId: STANDARD_OBJECTS.timelineActivity.morphIds.targetMorphId.morphId, fieldName: 'targetDashboard', - label: i18nLabel(msg`Target`), + label: i18nLabel(msg`Dashboard`), description: i18nLabel(msg`Event target`), - icon: 'IconArrowUpRight', + icon: 'IconTimelineEvent', isNullable: true, isUIEditable: false, + isSystemSideEffect: true, targetObjectName: 'dashboard', targetFieldName: 'timelineActivities', settings: { @@ -540,11 +549,12 @@ export const buildTimelineActivityStandardFlatFieldMetadatas = ({ type: FieldMetadataType.MORPH_RELATION, morphId: STANDARD_OBJECTS.timelineActivity.morphIds.targetMorphId.morphId, fieldName: 'targetMessageList', - label: i18nLabel(msg`Target`), + label: i18nLabel(msg`MessageList`), description: i18nLabel(msg`Event target`), - icon: 'IconArrowUpRight', + icon: 'IconTimelineEvent', isNullable: true, isUIEditable: false, + isSystemSideEffect: true, targetObjectName: 'messageList', targetFieldName: 'timelineActivities', settings: { @@ -565,11 +575,12 @@ export const buildTimelineActivityStandardFlatFieldMetadatas = ({ type: FieldMetadataType.MORPH_RELATION, morphId: STANDARD_OBJECTS.timelineActivity.morphIds.targetMorphId.morphId, fieldName: 'targetMessageCampaign', - label: i18nLabel(msg`Target`), + label: i18nLabel(msg`MessageCampaign`), description: i18nLabel(msg`Event target`), - icon: 'IconArrowUpRight', + icon: 'IconTimelineEvent', isNullable: true, isUIEditable: false, + isSystemSideEffect: true, targetObjectName: 'messageCampaign', targetFieldName: 'timelineActivities', settings: { diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-workflow-run-standard-flat-field-metadata.util.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-workflow-run-standard-flat-field-metadata.util.ts index 6ff53f9b4d..525c55e6ae 100644 --- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-workflow-run-standard-flat-field-metadata.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-workflow-run-standard-flat-field-metadata.util.ts @@ -420,6 +420,7 @@ export const buildWorkflowRunStandardFlatFieldMetadatas = ({ type: FieldMetadataType.RELATION, morphId: null, fieldName: 'timelineActivities', + isSystemSideEffect: true, label: i18nLabel( STANDARD_RELATION_FIELD_PROPERTIES_BY_RELATION_OBJECT.timelineActivity .label, diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-workflow-standard-flat-field-metadata.util.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-workflow-standard-flat-field-metadata.util.ts index ca99590b9a..c9d6a6544f 100644 --- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-workflow-standard-flat-field-metadata.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-workflow-standard-flat-field-metadata.util.ts @@ -357,6 +357,7 @@ export const buildWorkflowStandardFlatFieldMetadatas = ({ type: FieldMetadataType.RELATION, morphId: null, fieldName: 'timelineActivities', + isSystemSideEffect: true, label: i18nLabel( STANDARD_RELATION_FIELD_PROPERTIES_BY_RELATION_OBJECT.timelineActivity .label, @@ -383,6 +384,7 @@ export const buildWorkflowStandardFlatFieldMetadatas = ({ type: FieldMetadataType.RELATION, morphId: null, fieldName: 'attachments', + isSystemSideEffect: true, label: i18nLabel(msg`Attachments`), description: i18nLabel(msg`Attachments linked to the workflow`), icon: 'IconFileUpload', diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-workflow-version-standard-flat-field-metadata.util.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-workflow-version-standard-flat-field-metadata.util.ts index 7c44874109..1cb9bd5d42 100644 --- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-workflow-version-standard-flat-field-metadata.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/compute-workflow-version-standard-flat-field-metadata.util.ts @@ -364,6 +364,7 @@ export const buildWorkflowVersionStandardFlatFieldMetadatas = ({ type: FieldMetadataType.RELATION, morphId: null, fieldName: 'timelineActivities', + isSystemSideEffect: true, label: i18nLabel( STANDARD_RELATION_FIELD_PROPERTIES_BY_RELATION_OBJECT.timelineActivity .label, diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-relation-field-flat-metadata.util.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-relation-field-flat-metadata.util.ts index db322958cd..8dbbe73536 100644 --- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-relation-field-flat-metadata.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/field-metadata/create-standard-relation-field-flat-metadata.util.ts @@ -1,3 +1,9 @@ +import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type'; +import { type AllStandardObjectFieldName } from 'src/engine/workspace-manager/twenty-standard-application/types/all-standard-object-field-name.type'; +import { type AllStandardObjectName } from 'src/engine/workspace-manager/twenty-standard-application/types/all-standard-object-name.type'; +import { type StandardBuilderArgs } from 'src/engine/workspace-manager/twenty-standard-application/types/metadata-standard-buillder-args.type'; +import { TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER } from 'twenty-shared/application'; +import { STANDARD_OBJECTS } from 'twenty-shared/metadata'; import { type FieldMetadataComplexOption, type FieldMetadataDefaultOption, @@ -5,14 +11,6 @@ import { type FieldMetadataSettings, type FieldMetadataType, } from 'twenty-shared/types'; -import { STANDARD_OBJECTS } from 'twenty-shared/metadata'; -import { TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER } from 'twenty-shared/application'; - -import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type'; -import { type AllStandardObjectFieldName } from 'src/engine/workspace-manager/twenty-standard-application/types/all-standard-object-field-name.type'; -import { type AllStandardObjectName } from 'src/engine/workspace-manager/twenty-standard-application/types/all-standard-object-name.type'; -import { type StandardBuilderArgs } from 'src/engine/workspace-manager/twenty-standard-application/types/metadata-standard-buillder-args.type'; - export type CreateStandardRelationFieldContext< O extends AllStandardObjectName, T extends AllStandardObjectName, @@ -36,6 +34,7 @@ export type CreateStandardMorphOrRelationFieldContext< targetFieldName: AllStandardObjectFieldName; isNullable?: boolean; isUIEditable?: boolean; + isSystemSideEffect?: boolean; defaultValue?: FieldMetadataDefaultValueForAnyType; settings: FieldMetadataSettings; options?: FieldMetadataDefaultOption[] | FieldMetadataComplexOption[] | null; @@ -66,6 +65,7 @@ export const createStandardRelationFieldFlatMetadata = < targetFieldName, isNullable = true, isUIEditable = true, + isSystemSideEffect = false, defaultValue = null, settings, options: fieldOptions = null, @@ -101,7 +101,7 @@ export const createStandardRelationFieldFlatMetadata = < icon, isActive: true, isSystem: false, - isSystemSideEffect: false, + isSystemSideEffect, isNullable, isUnique: false, isUIEditable, diff --git a/packages/twenty-server/test/integration/metadata/suites/application/__snapshots__/failing-sync-application-default-relation-name-collision.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/application/__snapshots__/failing-sync-application-default-relation-name-collision.integration-spec.ts.snap new file mode 100644 index 0000000000..3ee3ab677b --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/application/__snapshots__/failing-sync-application-default-relation-name-collision.integration-spec.ts.snap @@ -0,0 +1,111 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Sync application default relation name collision should reject a manifest field named after a default relation when it carries a pinned universal identifier 1`] = ` +{ + "extensions": { + "code": "METADATA_VALIDATION_FAILED", + "errors": { + "fieldMetadata": [ + { + "errors": [ + { + "code": "NOT_AVAILABLE", + "message": "Name "attachments" is not available as it is already used by another field", + "userFriendlyMessage": "Name "attachments" is not available as it is already used by another field", + "value": "attachments", + }, + ], + "flatEntityMinimalInformation": { + "name": "attachments", + "objectMetadataUniversalIdentifier": Any, + "universalIdentifier": Any, + }, + "metadataName": "fieldMetadata", + "status": "fail", + "type": "create", + }, + { + "errors": [ + { + "code": "FIELD_METADATA_NOT_FOUND", + "message": "Relation field target metadata not found in both existing and about to be created field metadatas", + "userFriendlyMessage": "Relation field target metadata not found", + }, + ], + "flatEntityMinimalInformation": { + "name": "targetDefaultRelationCollisionTarget", + "objectMetadataUniversalIdentifier": Any, + "universalIdentifier": Any, + }, + "metadataName": "fieldMetadata", + "status": "fail", + "type": "create", + }, + ], + "index": [ + { + "errors": [ + { + "code": "INDEX_FIELD_NOT_FOUND", + "message": "Could not find index field related field metadata", + "userFriendlyMessage": "Field referenced in index does not exist", + }, + ], + "flatEntityMinimalInformation": { + "name": "IDX_6d15112a8688389df51c6dfc321", + "universalIdentifier": Any, + }, + "metadataName": "index", + "status": "fail", + "type": "create", + }, + ], + }, + "message": "Validation failed for 2 fieldMetadata, 1 index", + "summary": { + "fieldMetadata": 2, + "index": 1, + "totalErrors": 3, + }, + "userFriendlyMessage": "Many validation errors", + }, + "message": "Validation errors occurred while syncing application manifest metadata", + "name": "GraphQLError", +} +`; + +exports[`Sync application default relation name collision should reject a manifest field named after a default relation when it carries the derived universal identifier 1`] = ` +{ + "extensions": { + "code": "METADATA_VALIDATION_FAILED", + "errors": { + "fieldMetadata": [ + { + "errors": [ + { + "code": "RESERVED_SYSTEM_UNIVERSAL_IDENTIFIER", + "message": "Universal identifier is reserved for system-managed metadata", + "userFriendlyMessage": "This identifier is reserved by the system", + }, + ], + "flatEntityMinimalInformation": { + "name": "attachments", + "universalIdentifier": Any, + }, + "metadataName": "fieldMetadata", + "status": "fail", + "type": "create", + }, + ], + }, + "message": "Validation failed for 1 fieldMetadata", + "summary": { + "fieldMetadata": 1, + "totalErrors": 1, + }, + "userFriendlyMessage": "This identifier is reserved by the system", + }, + "message": "Validation errors occurred while syncing application manifest metadata", + "name": "GraphQLError", +} +`; diff --git a/packages/twenty-server/test/integration/metadata/suites/application/__snapshots__/failing-sync-application-search-vector-identifier-hijack.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/application/__snapshots__/failing-sync-application-search-vector-identifier-hijack.integration-spec.ts.snap new file mode 100644 index 0000000000..ef831e9f3b --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/application/__snapshots__/failing-sync-application-search-vector-identifier-hijack.integration-spec.ts.snap @@ -0,0 +1,119 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Sync application should reject hijacking the searchVector system field should reject a manifest field declared under the reserved searchVector name 1`] = ` +{ + "extensions": { + "code": "METADATA_VALIDATION_FAILED", + "errors": { + "fieldMetadata": [ + { + "errors": [ + { + "code": "FIELD_MUTATION_NOT_ALLOWED", + "message": "System fields only allow updating: universalSettings, isActive. Forbidden properties: description, icon, isUIEditable", + "userFriendlyMessage": "System fields cannot be updated", + }, + ], + "flatEntityMinimalInformation": { + "name": "searchVector", + "objectMetadataUniversalIdentifier": Any, + "universalIdentifier": Any, + }, + "metadataName": "fieldMetadata", + "status": "fail", + "type": "update", + }, + ], + }, + "message": "Validation failed for 1 fieldMetadata", + "summary": { + "fieldMetadata": 1, + "totalErrors": 1, + }, + "userFriendlyMessage": "System fields cannot be updated", + }, + "message": "Validation errors occurred while syncing application manifest metadata", + "name": "GraphQLError", +} +`; + +exports[`Sync application should reject hijacking the searchVector system field should reject a manifest field reusing the searchVector name under a foreign universal identifier 1`] = ` +{ + "extensions": { + "code": "METADATA_VALIDATION_FAILED", + "errors": { + "fieldMetadata": [ + { + "errors": [ + { + "code": "NOT_AVAILABLE", + "message": "Name "searchVector" is not available as it is already used by another field", + "userFriendlyMessage": "Name "searchVector" is not available as it is already used by another field", + "value": "searchVector", + }, + ], + "flatEntityMinimalInformation": { + "name": "searchVector", + "objectMetadataUniversalIdentifier": Any, + "universalIdentifier": Any, + }, + "metadataName": "fieldMetadata", + "status": "fail", + "type": "create", + }, + ], + }, + "message": "Validation failed for 1 fieldMetadata", + "summary": { + "fieldMetadata": 1, + "totalErrors": 1, + }, + "userFriendlyMessage": "Name "searchVector" is not available as it is already used by another field", + }, + "message": "Validation errors occurred while syncing application manifest metadata", + "name": "GraphQLError", +} +`; + +exports[`Sync application should reject hijacking the searchVector system field should reject a manifest field squatting the searchVector deterministic universal identifier 1`] = ` +{ + "extensions": { + "code": "METADATA_VALIDATION_FAILED", + "errors": { + "fieldMetadata": [ + { + "errors": [ + { + "code": "FIELD_MUTATION_NOT_ALLOWED", + "message": "System fields only allow updating: universalSettings, isActive. Forbidden properties: description, icon, label, name, isUIEditable", + "userFriendlyMessage": "System fields cannot be updated", + }, + { + "code": "INVALID_FIELD_INPUT", + "message": "Field type TS_VECTOR must be named "searchVector", got "stolenSearchVector"", + "userFriendlyMessage": "Field type TS_VECTOR must be named "searchVector"", + "value": "stolenSearchVector", + }, + ], + "flatEntityMinimalInformation": { + "name": "stolenSearchVector", + "objectMetadataUniversalIdentifier": Any, + "universalIdentifier": Any, + }, + "metadataName": "fieldMetadata", + "status": "fail", + "type": "update", + }, + ], + }, + "message": "Validation failed for 1 fieldMetadata", + "summary": { + "fieldMetadata": 1, + "totalErrors": 1, + }, + "userFriendlyMessage": "System fields cannot be updated", + }, + "message": "Validation errors occurred while syncing application manifest metadata", + "name": "GraphQLError", +} +`; diff --git a/packages/twenty-server/test/integration/metadata/suites/application/__snapshots__/successful-sync-application-workspace-migration.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/application/__snapshots__/successful-sync-application-workspace-migration.integration-spec.ts.snap index 8ce400122a..c38a9ea0fc 100644 --- a/packages/twenty-server/test/integration/metadata/suites/application/__snapshots__/successful-sync-application-workspace-migration.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/metadata/suites/application/__snapshots__/successful-sync-application-workspace-migration.integration-spec.ts.snap @@ -101,224 +101,6 @@ exports[`syncApplication should delete old field and create equivalent one when "metadataName": "objectMetadata", "type": "create", "universalFlatFieldMetadatas": [ - { - "applicationUniversalIdentifier": Any, - "createdAt": Any, - "defaultValue": null, - "description": null, - "icon": null, - "isActive": true, - "isLabelSyncedWithName": false, - "isNullable": true, - "isSystem": true, - "isSystemSideEffect": true, - "isUIEditable": true, - "isUnique": false, - "label": "Id", - "morphId": null, - "name": "id", - "objectMetadataUniversalIdentifier": Any, - "options": null, - "overrides": null, - "relationTargetFieldMetadataUniversalIdentifier": null, - "relationTargetObjectMetadataUniversalIdentifier": null, - "type": "UUID", - "universalIdentifier": Any, - "universalSettings": null, - "updatedAt": Any, - }, - { - "applicationUniversalIdentifier": Any, - "createdAt": Any, - "defaultValue": null, - "description": null, - "icon": null, - "isActive": true, - "isLabelSyncedWithName": false, - "isNullable": true, - "isSystem": true, - "isSystemSideEffect": true, - "isUIEditable": true, - "isUnique": false, - "label": "Creation date", - "morphId": null, - "name": "createdAt", - "objectMetadataUniversalIdentifier": Any, - "options": null, - "overrides": null, - "relationTargetFieldMetadataUniversalIdentifier": null, - "relationTargetObjectMetadataUniversalIdentifier": null, - "type": "DATE_TIME", - "universalIdentifier": Any, - "universalSettings": null, - "updatedAt": Any, - }, - { - "applicationUniversalIdentifier": Any, - "createdAt": Any, - "defaultValue": null, - "description": null, - "icon": null, - "isActive": true, - "isLabelSyncedWithName": false, - "isNullable": true, - "isSystem": true, - "isSystemSideEffect": true, - "isUIEditable": true, - "isUnique": false, - "label": "Last update", - "morphId": null, - "name": "updatedAt", - "objectMetadataUniversalIdentifier": Any, - "options": null, - "overrides": null, - "relationTargetFieldMetadataUniversalIdentifier": null, - "relationTargetObjectMetadataUniversalIdentifier": null, - "type": "DATE_TIME", - "universalIdentifier": Any, - "universalSettings": null, - "updatedAt": Any, - }, - { - "applicationUniversalIdentifier": Any, - "createdAt": Any, - "defaultValue": null, - "description": null, - "icon": null, - "isActive": true, - "isLabelSyncedWithName": false, - "isNullable": true, - "isSystem": true, - "isSystemSideEffect": true, - "isUIEditable": true, - "isUnique": false, - "label": "Deleted at", - "morphId": null, - "name": "deletedAt", - "objectMetadataUniversalIdentifier": Any, - "options": null, - "overrides": null, - "relationTargetFieldMetadataUniversalIdentifier": null, - "relationTargetObjectMetadataUniversalIdentifier": null, - "type": "DATE_TIME", - "universalIdentifier": Any, - "universalSettings": null, - "updatedAt": Any, - }, - { - "applicationUniversalIdentifier": Any, - "createdAt": Any, - "defaultValue": { - "context": null, - "name": "'System'", - "source": "'MANUAL'", - "workspaceMemberId": null, - }, - "description": null, - "icon": null, - "isActive": true, - "isLabelSyncedWithName": false, - "isNullable": true, - "isSystem": true, - "isSystemSideEffect": true, - "isUIEditable": true, - "isUnique": false, - "label": "Created by", - "morphId": null, - "name": "createdBy", - "objectMetadataUniversalIdentifier": Any, - "options": null, - "overrides": null, - "relationTargetFieldMetadataUniversalIdentifier": null, - "relationTargetObjectMetadataUniversalIdentifier": null, - "type": "ACTOR", - "universalIdentifier": Any, - "universalSettings": null, - "updatedAt": Any, - }, - { - "applicationUniversalIdentifier": Any, - "createdAt": Any, - "defaultValue": { - "context": null, - "name": "'System'", - "source": "'MANUAL'", - "workspaceMemberId": null, - }, - "description": null, - "icon": null, - "isActive": true, - "isLabelSyncedWithName": false, - "isNullable": true, - "isSystem": true, - "isSystemSideEffect": true, - "isUIEditable": true, - "isUnique": false, - "label": "Updated by", - "morphId": null, - "name": "updatedBy", - "objectMetadataUniversalIdentifier": Any, - "options": null, - "overrides": null, - "relationTargetFieldMetadataUniversalIdentifier": null, - "relationTargetObjectMetadataUniversalIdentifier": null, - "type": "ACTOR", - "universalIdentifier": Any, - "universalSettings": null, - "updatedAt": Any, - }, - { - "applicationUniversalIdentifier": Any, - "createdAt": Any, - "defaultValue": null, - "description": null, - "icon": null, - "isActive": true, - "isLabelSyncedWithName": false, - "isNullable": true, - "isSystem": true, - "isSystemSideEffect": true, - "isUIEditable": true, - "isUnique": false, - "label": "Position", - "morphId": null, - "name": "position", - "objectMetadataUniversalIdentifier": Any, - "options": null, - "overrides": null, - "relationTargetFieldMetadataUniversalIdentifier": null, - "relationTargetObjectMetadataUniversalIdentifier": null, - "type": "POSITION", - "universalIdentifier": Any, - "universalSettings": null, - "updatedAt": Any, - }, - { - "applicationUniversalIdentifier": Any, - "createdAt": Any, - "defaultValue": null, - "description": null, - "icon": null, - "isActive": true, - "isLabelSyncedWithName": false, - "isNullable": true, - "isSystem": true, - "isSystemSideEffect": true, - "isUIEditable": true, - "isUnique": false, - "label": "Search vector", - "morphId": null, - "name": "searchVector", - "objectMetadataUniversalIdentifier": Any, - "options": null, - "overrides": null, - "relationTargetFieldMetadataUniversalIdentifier": null, - "relationTargetObjectMetadataUniversalIdentifier": null, - "type": "TS_VECTOR", - "universalIdentifier": Any, - "universalSettings": null, - "updatedAt": Any, - }, { "applicationUniversalIdentifier": Any, "createdAt": Any, @@ -345,8 +127,590 @@ exports[`syncApplication should delete old field and create equivalent one when "universalSettings": null, "updatedAt": Any, }, + { + "applicationUniversalIdentifier": Any, + "createdAt": Any, + "defaultValue": "uuid", + "description": "Id", + "icon": "Icon123", + "isActive": true, + "isLabelSyncedWithName": false, + "isNullable": false, + "isSystem": true, + "isSystemSideEffect": true, + "isUIEditable": false, + "isUnique": true, + "label": "Id", + "morphId": null, + "name": "id", + "objectMetadataUniversalIdentifier": Any, + "options": null, + "overrides": null, + "relationTargetFieldMetadataUniversalIdentifier": null, + "relationTargetObjectMetadataUniversalIdentifier": null, + "type": "UUID", + "universalIdentifier": Any, + "universalSettings": null, + "updatedAt": Any, + }, + { + "applicationUniversalIdentifier": Any, + "createdAt": Any, + "defaultValue": "now", + "description": "Creation date", + "icon": "IconCalendar", + "isActive": true, + "isLabelSyncedWithName": false, + "isNullable": false, + "isSystem": true, + "isSystemSideEffect": true, + "isUIEditable": false, + "isUnique": false, + "label": "Creation date", + "morphId": null, + "name": "createdAt", + "objectMetadataUniversalIdentifier": Any, + "options": null, + "overrides": null, + "relationTargetFieldMetadataUniversalIdentifier": null, + "relationTargetObjectMetadataUniversalIdentifier": null, + "type": "DATE_TIME", + "universalIdentifier": Any, + "universalSettings": null, + "updatedAt": Any, + }, + { + "applicationUniversalIdentifier": Any, + "createdAt": Any, + "defaultValue": { + "name": "''", + "source": "'MANUAL'", + }, + "description": "The creator of the record", + "icon": "IconCreativeCommonsSa", + "isActive": true, + "isLabelSyncedWithName": false, + "isNullable": false, + "isSystem": true, + "isSystemSideEffect": true, + "isUIEditable": false, + "isUnique": false, + "label": "Created by", + "morphId": null, + "name": "createdBy", + "objectMetadataUniversalIdentifier": Any, + "options": null, + "overrides": null, + "relationTargetFieldMetadataUniversalIdentifier": null, + "relationTargetObjectMetadataUniversalIdentifier": null, + "type": "ACTOR", + "universalIdentifier": Any, + "universalSettings": null, + "updatedAt": Any, + }, + { + "applicationUniversalIdentifier": Any, + "createdAt": Any, + "defaultValue": null, + "description": "Deletion date", + "icon": "IconCalendarClock", + "isActive": true, + "isLabelSyncedWithName": false, + "isNullable": true, + "isSystem": true, + "isSystemSideEffect": true, + "isUIEditable": false, + "isUnique": false, + "label": "Deleted at", + "morphId": null, + "name": "deletedAt", + "objectMetadataUniversalIdentifier": Any, + "options": null, + "overrides": null, + "relationTargetFieldMetadataUniversalIdentifier": null, + "relationTargetObjectMetadataUniversalIdentifier": null, + "type": "DATE_TIME", + "universalIdentifier": Any, + "universalSettings": null, + "updatedAt": Any, + }, + { + "applicationUniversalIdentifier": Any, + "createdAt": Any, + "defaultValue": 0, + "description": "Position", + "icon": "IconHierarchy2", + "isActive": true, + "isLabelSyncedWithName": false, + "isNullable": false, + "isSystem": true, + "isSystemSideEffect": true, + "isUIEditable": false, + "isUnique": false, + "label": "Position", + "morphId": null, + "name": "position", + "objectMetadataUniversalIdentifier": Any, + "options": null, + "overrides": null, + "relationTargetFieldMetadataUniversalIdentifier": null, + "relationTargetObjectMetadataUniversalIdentifier": null, + "type": "POSITION", + "universalIdentifier": Any, + "universalSettings": null, + "updatedAt": Any, + }, + { + "applicationUniversalIdentifier": Any, + "createdAt": Any, + "defaultValue": "now", + "description": "Last time the record was changed", + "icon": "IconCalendarClock", + "isActive": true, + "isLabelSyncedWithName": false, + "isNullable": false, + "isSystem": true, + "isSystemSideEffect": true, + "isUIEditable": false, + "isUnique": false, + "label": "Last update", + "morphId": null, + "name": "updatedAt", + "objectMetadataUniversalIdentifier": Any, + "options": null, + "overrides": null, + "relationTargetFieldMetadataUniversalIdentifier": null, + "relationTargetObjectMetadataUniversalIdentifier": null, + "type": "DATE_TIME", + "universalIdentifier": Any, + "universalSettings": null, + "updatedAt": Any, + }, + { + "applicationUniversalIdentifier": Any, + "createdAt": Any, + "defaultValue": { + "name": "''", + "source": "'MANUAL'", + }, + "description": "The workspace member who last updated the record", + "icon": "IconUserCircle", + "isActive": true, + "isLabelSyncedWithName": false, + "isNullable": false, + "isSystem": true, + "isSystemSideEffect": true, + "isUIEditable": false, + "isUnique": false, + "label": "Updated by", + "morphId": null, + "name": "updatedBy", + "objectMetadataUniversalIdentifier": Any, + "options": null, + "overrides": null, + "relationTargetFieldMetadataUniversalIdentifier": null, + "relationTargetObjectMetadataUniversalIdentifier": null, + "type": "ACTOR", + "universalIdentifier": Any, + "universalSettings": null, + "updatedAt": Any, + }, + { + "applicationUniversalIdentifier": Any, + "createdAt": Any, + "defaultValue": null, + "description": "Search vector", + "icon": "IconSearch", + "isActive": true, + "isLabelSyncedWithName": false, + "isNullable": true, + "isSystem": true, + "isSystemSideEffect": true, + "isUIEditable": false, + "isUnique": false, + "label": "Search vector", + "morphId": null, + "name": "searchVector", + "objectMetadataUniversalIdentifier": Any, + "options": null, + "overrides": null, + "relationTargetFieldMetadataUniversalIdentifier": null, + "relationTargetObjectMetadataUniversalIdentifier": null, + "type": "TS_VECTOR", + "universalIdentifier": Any, + "universalSettings": null, + "updatedAt": Any, + }, ], }, + { + "fieldIdByUniversalIdentifier": Any, + "flatEntity": { + "applicationUniversalIdentifier": Any, + "createdAt": Any, + "defaultValue": null, + "description": "Tickets tied to the Timeline Activity", + "icon": "IconTimelineEvent", + "isActive": true, + "isLabelSyncedWithName": false, + "isNullable": true, + "isSystem": false, + "isSystemSideEffect": true, + "isUIEditable": true, + "isUnique": false, + "label": "Timeline Activities", + "morphId": null, + "name": "timelineActivities", + "objectMetadataUniversalIdentifier": Any, + "options": null, + "overrides": null, + "relationTargetFieldMetadataUniversalIdentifier": Any, + "relationTargetObjectMetadataUniversalIdentifier": Any, + "type": "RELATION", + "universalIdentifier": Any, + "universalSettings": { + "relationType": "ONE_TO_MANY", + }, + "updatedAt": Any, + }, + "id": Any, + "metadataName": "fieldMetadata", + "relatedFieldId": Any, + "relatedUniversalFlatFieldMetadata": { + "applicationUniversalIdentifier": Any, + "createdAt": Any, + "defaultValue": null, + "description": "TimelineActivities Ticket", + "icon": "IconTimelineEvent", + "isActive": true, + "isLabelSyncedWithName": false, + "isNullable": true, + "isSystem": false, + "isSystemSideEffect": true, + "isUIEditable": true, + "isUnique": false, + "label": "Ticket", + "morphId": Any, + "name": "targetTicket", + "objectMetadataUniversalIdentifier": Any, + "options": null, + "overrides": null, + "relationTargetFieldMetadataUniversalIdentifier": Any, + "relationTargetObjectMetadataUniversalIdentifier": Any, + "type": "MORPH_RELATION", + "universalIdentifier": Any, + "universalSettings": { + "joinColumnName": "targetTicketId", + "onDelete": "SET_NULL", + "relationType": "MANY_TO_ONE", + }, + "updatedAt": Any, + }, + "type": "create", + }, + { + "fieldIdByUniversalIdentifier": Any, + "flatEntity": { + "applicationUniversalIdentifier": Any, + "createdAt": Any, + "defaultValue": null, + "description": "Tickets tied to the Attachment", + "icon": "IconFileImport", + "isActive": true, + "isLabelSyncedWithName": false, + "isNullable": true, + "isSystem": false, + "isSystemSideEffect": true, + "isUIEditable": true, + "isUnique": false, + "label": "Attachments", + "morphId": null, + "name": "attachments", + "objectMetadataUniversalIdentifier": Any, + "options": null, + "overrides": null, + "relationTargetFieldMetadataUniversalIdentifier": Any, + "relationTargetObjectMetadataUniversalIdentifier": Any, + "type": "RELATION", + "universalIdentifier": Any, + "universalSettings": { + "relationType": "ONE_TO_MANY", + }, + "updatedAt": Any, + }, + "id": Any, + "metadataName": "fieldMetadata", + "relatedFieldId": Any, + "relatedUniversalFlatFieldMetadata": { + "applicationUniversalIdentifier": Any, + "createdAt": Any, + "defaultValue": null, + "description": "Attachments Ticket", + "icon": "IconFileImport", + "isActive": true, + "isLabelSyncedWithName": false, + "isNullable": true, + "isSystem": false, + "isSystemSideEffect": true, + "isUIEditable": true, + "isUnique": false, + "label": "Ticket", + "morphId": Any, + "name": "targetTicket", + "objectMetadataUniversalIdentifier": Any, + "options": null, + "overrides": null, + "relationTargetFieldMetadataUniversalIdentifier": Any, + "relationTargetObjectMetadataUniversalIdentifier": Any, + "type": "MORPH_RELATION", + "universalIdentifier": Any, + "universalSettings": { + "joinColumnName": "targetTicketId", + "onDelete": "SET_NULL", + "relationType": "MANY_TO_ONE", + }, + "updatedAt": Any, + }, + "type": "create", + }, + { + "fieldIdByUniversalIdentifier": Any, + "flatEntity": { + "applicationUniversalIdentifier": Any, + "createdAt": Any, + "defaultValue": null, + "description": "Tickets tied to the Note Target", + "icon": "IconNotes", + "isActive": true, + "isLabelSyncedWithName": false, + "isNullable": true, + "isSystem": false, + "isSystemSideEffect": true, + "isUIEditable": true, + "isUnique": false, + "label": "Notes", + "morphId": null, + "name": "noteTargets", + "objectMetadataUniversalIdentifier": Any, + "options": null, + "overrides": null, + "relationTargetFieldMetadataUniversalIdentifier": Any, + "relationTargetObjectMetadataUniversalIdentifier": Any, + "type": "RELATION", + "universalIdentifier": Any, + "universalSettings": { + "relationType": "ONE_TO_MANY", + }, + "updatedAt": Any, + }, + "id": Any, + "metadataName": "fieldMetadata", + "relatedFieldId": Any, + "relatedUniversalFlatFieldMetadata": { + "applicationUniversalIdentifier": Any, + "createdAt": Any, + "defaultValue": null, + "description": "NoteTargets Ticket", + "icon": "IconCheckbox", + "isActive": true, + "isLabelSyncedWithName": false, + "isNullable": true, + "isSystem": false, + "isSystemSideEffect": true, + "isUIEditable": true, + "isUnique": false, + "label": "Ticket", + "morphId": Any, + "name": "targetTicket", + "objectMetadataUniversalIdentifier": Any, + "options": null, + "overrides": null, + "relationTargetFieldMetadataUniversalIdentifier": Any, + "relationTargetObjectMetadataUniversalIdentifier": Any, + "type": "MORPH_RELATION", + "universalIdentifier": Any, + "universalSettings": { + "joinColumnName": "targetTicketId", + "onDelete": "SET_NULL", + "relationType": "MANY_TO_ONE", + }, + "updatedAt": Any, + }, + "type": "create", + }, + { + "fieldIdByUniversalIdentifier": Any, + "flatEntity": { + "applicationUniversalIdentifier": Any, + "createdAt": Any, + "defaultValue": null, + "description": "Tickets tied to the Task Target", + "icon": "IconCheckbox", + "isActive": true, + "isLabelSyncedWithName": false, + "isNullable": true, + "isSystem": false, + "isSystemSideEffect": true, + "isUIEditable": true, + "isUnique": false, + "label": "Tasks", + "morphId": null, + "name": "taskTargets", + "objectMetadataUniversalIdentifier": Any, + "options": null, + "overrides": null, + "relationTargetFieldMetadataUniversalIdentifier": Any, + "relationTargetObjectMetadataUniversalIdentifier": Any, + "type": "RELATION", + "universalIdentifier": Any, + "universalSettings": { + "relationType": "ONE_TO_MANY", + }, + "updatedAt": Any, + }, + "id": Any, + "metadataName": "fieldMetadata", + "relatedFieldId": Any, + "relatedUniversalFlatFieldMetadata": { + "applicationUniversalIdentifier": Any, + "createdAt": Any, + "defaultValue": null, + "description": "TaskTargets Ticket", + "icon": "IconCheckbox", + "isActive": true, + "isLabelSyncedWithName": false, + "isNullable": true, + "isSystem": false, + "isSystemSideEffect": true, + "isUIEditable": true, + "isUnique": false, + "label": "Ticket", + "morphId": Any, + "name": "targetTicket", + "objectMetadataUniversalIdentifier": Any, + "options": null, + "overrides": null, + "relationTargetFieldMetadataUniversalIdentifier": Any, + "relationTargetObjectMetadataUniversalIdentifier": Any, + "type": "MORPH_RELATION", + "universalIdentifier": Any, + "universalSettings": { + "joinColumnName": "targetTicketId", + "onDelete": "SET_NULL", + "relationType": "MANY_TO_ONE", + }, + "updatedAt": Any, + }, + "type": "create", + }, + { + "flatEntity": { + "applicationUniversalIdentifier": Any, + "createdAt": Any, + "indexType": "BTREE", + "indexWhereClause": null, + "isCustom": true, + "isSystemSideEffect": true, + "isUnique": false, + "name": "IDX_8da02734dc044074a6a2c224f2a", + "objectMetadataUniversalIdentifier": Any, + "universalFlatIndexFieldMetadatas": [ + { + "createdAt": Any, + "fieldMetadataUniversalIdentifier": Any, + "indexMetadataUniversalIdentifier": Any, + "order": 0, + "subFieldName": null, + "updatedAt": Any, + }, + ], + "universalIdentifier": Any, + "updatedAt": Any, + }, + "metadataName": "index", + "type": "create", + }, + { + "flatEntity": { + "applicationUniversalIdentifier": Any, + "createdAt": Any, + "indexType": "BTREE", + "indexWhereClause": null, + "isCustom": true, + "isSystemSideEffect": true, + "isUnique": false, + "name": "IDX_890a5ffa99e9b995803d50f2298", + "objectMetadataUniversalIdentifier": Any, + "universalFlatIndexFieldMetadatas": [ + { + "createdAt": Any, + "fieldMetadataUniversalIdentifier": Any, + "indexMetadataUniversalIdentifier": Any, + "order": 0, + "subFieldName": null, + "updatedAt": Any, + }, + ], + "universalIdentifier": Any, + "updatedAt": Any, + }, + "metadataName": "index", + "type": "create", + }, + { + "flatEntity": { + "applicationUniversalIdentifier": Any, + "createdAt": Any, + "indexType": "BTREE", + "indexWhereClause": null, + "isCustom": true, + "isSystemSideEffect": true, + "isUnique": false, + "name": "IDX_d2022a25ca76206ddf16d88efd5", + "objectMetadataUniversalIdentifier": Any, + "universalFlatIndexFieldMetadatas": [ + { + "createdAt": Any, + "fieldMetadataUniversalIdentifier": Any, + "indexMetadataUniversalIdentifier": Any, + "order": 0, + "subFieldName": null, + "updatedAt": Any, + }, + ], + "universalIdentifier": Any, + "updatedAt": Any, + }, + "metadataName": "index", + "type": "create", + }, + { + "flatEntity": { + "applicationUniversalIdentifier": Any, + "createdAt": Any, + "indexType": "BTREE", + "indexWhereClause": null, + "isCustom": true, + "isSystemSideEffect": true, + "isUnique": false, + "name": "IDX_7d7a4e9e0805030aee982fdfa31", + "objectMetadataUniversalIdentifier": Any, + "universalFlatIndexFieldMetadatas": [ + { + "createdAt": Any, + "fieldMetadataUniversalIdentifier": Any, + "indexMetadataUniversalIdentifier": Any, + "order": 0, + "subFieldName": null, + "updatedAt": Any, + }, + ], + "universalIdentifier": Any, + "updatedAt": Any, + }, + "metadataName": "index", + "type": "create", + }, { "flatEntity": { "applicationUniversalIdentifier": Any, @@ -537,224 +901,6 @@ exports[`syncApplication should return workspace migration actions on initial sy "metadataName": "objectMetadata", "type": "create", "universalFlatFieldMetadatas": [ - { - "applicationUniversalIdentifier": Any, - "createdAt": Any, - "defaultValue": null, - "description": null, - "icon": null, - "isActive": true, - "isLabelSyncedWithName": false, - "isNullable": true, - "isSystem": true, - "isSystemSideEffect": true, - "isUIEditable": true, - "isUnique": false, - "label": "Id", - "morphId": null, - "name": "id", - "objectMetadataUniversalIdentifier": Any, - "options": null, - "overrides": null, - "relationTargetFieldMetadataUniversalIdentifier": null, - "relationTargetObjectMetadataUniversalIdentifier": null, - "type": "UUID", - "universalIdentifier": Any, - "universalSettings": null, - "updatedAt": Any, - }, - { - "applicationUniversalIdentifier": Any, - "createdAt": Any, - "defaultValue": null, - "description": null, - "icon": null, - "isActive": true, - "isLabelSyncedWithName": false, - "isNullable": true, - "isSystem": true, - "isSystemSideEffect": true, - "isUIEditable": true, - "isUnique": false, - "label": "Creation date", - "morphId": null, - "name": "createdAt", - "objectMetadataUniversalIdentifier": Any, - "options": null, - "overrides": null, - "relationTargetFieldMetadataUniversalIdentifier": null, - "relationTargetObjectMetadataUniversalIdentifier": null, - "type": "DATE_TIME", - "universalIdentifier": Any, - "universalSettings": null, - "updatedAt": Any, - }, - { - "applicationUniversalIdentifier": Any, - "createdAt": Any, - "defaultValue": null, - "description": null, - "icon": null, - "isActive": true, - "isLabelSyncedWithName": false, - "isNullable": true, - "isSystem": true, - "isSystemSideEffect": true, - "isUIEditable": true, - "isUnique": false, - "label": "Last update", - "morphId": null, - "name": "updatedAt", - "objectMetadataUniversalIdentifier": Any, - "options": null, - "overrides": null, - "relationTargetFieldMetadataUniversalIdentifier": null, - "relationTargetObjectMetadataUniversalIdentifier": null, - "type": "DATE_TIME", - "universalIdentifier": Any, - "universalSettings": null, - "updatedAt": Any, - }, - { - "applicationUniversalIdentifier": Any, - "createdAt": Any, - "defaultValue": null, - "description": null, - "icon": null, - "isActive": true, - "isLabelSyncedWithName": false, - "isNullable": true, - "isSystem": true, - "isSystemSideEffect": true, - "isUIEditable": true, - "isUnique": false, - "label": "Deleted at", - "morphId": null, - "name": "deletedAt", - "objectMetadataUniversalIdentifier": Any, - "options": null, - "overrides": null, - "relationTargetFieldMetadataUniversalIdentifier": null, - "relationTargetObjectMetadataUniversalIdentifier": null, - "type": "DATE_TIME", - "universalIdentifier": Any, - "universalSettings": null, - "updatedAt": Any, - }, - { - "applicationUniversalIdentifier": Any, - "createdAt": Any, - "defaultValue": { - "context": null, - "name": "'System'", - "source": "'MANUAL'", - "workspaceMemberId": null, - }, - "description": null, - "icon": null, - "isActive": true, - "isLabelSyncedWithName": false, - "isNullable": true, - "isSystem": true, - "isSystemSideEffect": true, - "isUIEditable": true, - "isUnique": false, - "label": "Created by", - "morphId": null, - "name": "createdBy", - "objectMetadataUniversalIdentifier": Any, - "options": null, - "overrides": null, - "relationTargetFieldMetadataUniversalIdentifier": null, - "relationTargetObjectMetadataUniversalIdentifier": null, - "type": "ACTOR", - "universalIdentifier": Any, - "universalSettings": null, - "updatedAt": Any, - }, - { - "applicationUniversalIdentifier": Any, - "createdAt": Any, - "defaultValue": { - "context": null, - "name": "'System'", - "source": "'MANUAL'", - "workspaceMemberId": null, - }, - "description": null, - "icon": null, - "isActive": true, - "isLabelSyncedWithName": false, - "isNullable": true, - "isSystem": true, - "isSystemSideEffect": true, - "isUIEditable": true, - "isUnique": false, - "label": "Updated by", - "morphId": null, - "name": "updatedBy", - "objectMetadataUniversalIdentifier": Any, - "options": null, - "overrides": null, - "relationTargetFieldMetadataUniversalIdentifier": null, - "relationTargetObjectMetadataUniversalIdentifier": null, - "type": "ACTOR", - "universalIdentifier": Any, - "universalSettings": null, - "updatedAt": Any, - }, - { - "applicationUniversalIdentifier": Any, - "createdAt": Any, - "defaultValue": null, - "description": null, - "icon": null, - "isActive": true, - "isLabelSyncedWithName": false, - "isNullable": true, - "isSystem": true, - "isSystemSideEffect": true, - "isUIEditable": true, - "isUnique": false, - "label": "Position", - "morphId": null, - "name": "position", - "objectMetadataUniversalIdentifier": Any, - "options": null, - "overrides": null, - "relationTargetFieldMetadataUniversalIdentifier": null, - "relationTargetObjectMetadataUniversalIdentifier": null, - "type": "POSITION", - "universalIdentifier": Any, - "universalSettings": null, - "updatedAt": Any, - }, - { - "applicationUniversalIdentifier": Any, - "createdAt": Any, - "defaultValue": null, - "description": null, - "icon": null, - "isActive": true, - "isLabelSyncedWithName": false, - "isNullable": true, - "isSystem": true, - "isSystemSideEffect": true, - "isUIEditable": true, - "isUnique": false, - "label": "Search vector", - "morphId": null, - "name": "searchVector", - "objectMetadataUniversalIdentifier": Any, - "options": null, - "overrides": null, - "relationTargetFieldMetadataUniversalIdentifier": null, - "relationTargetObjectMetadataUniversalIdentifier": null, - "type": "TS_VECTOR", - "universalIdentifier": Any, - "universalSettings": null, - "updatedAt": Any, - }, { "applicationUniversalIdentifier": Any, "createdAt": Any, @@ -781,8 +927,590 @@ exports[`syncApplication should return workspace migration actions on initial sy "universalSettings": null, "updatedAt": Any, }, + { + "applicationUniversalIdentifier": Any, + "createdAt": Any, + "defaultValue": "uuid", + "description": "Id", + "icon": "Icon123", + "isActive": true, + "isLabelSyncedWithName": false, + "isNullable": false, + "isSystem": true, + "isSystemSideEffect": true, + "isUIEditable": false, + "isUnique": true, + "label": "Id", + "morphId": null, + "name": "id", + "objectMetadataUniversalIdentifier": Any, + "options": null, + "overrides": null, + "relationTargetFieldMetadataUniversalIdentifier": null, + "relationTargetObjectMetadataUniversalIdentifier": null, + "type": "UUID", + "universalIdentifier": Any, + "universalSettings": null, + "updatedAt": Any, + }, + { + "applicationUniversalIdentifier": Any, + "createdAt": Any, + "defaultValue": "now", + "description": "Creation date", + "icon": "IconCalendar", + "isActive": true, + "isLabelSyncedWithName": false, + "isNullable": false, + "isSystem": true, + "isSystemSideEffect": true, + "isUIEditable": false, + "isUnique": false, + "label": "Creation date", + "morphId": null, + "name": "createdAt", + "objectMetadataUniversalIdentifier": Any, + "options": null, + "overrides": null, + "relationTargetFieldMetadataUniversalIdentifier": null, + "relationTargetObjectMetadataUniversalIdentifier": null, + "type": "DATE_TIME", + "universalIdentifier": Any, + "universalSettings": null, + "updatedAt": Any, + }, + { + "applicationUniversalIdentifier": Any, + "createdAt": Any, + "defaultValue": { + "name": "''", + "source": "'MANUAL'", + }, + "description": "The creator of the record", + "icon": "IconCreativeCommonsSa", + "isActive": true, + "isLabelSyncedWithName": false, + "isNullable": false, + "isSystem": true, + "isSystemSideEffect": true, + "isUIEditable": false, + "isUnique": false, + "label": "Created by", + "morphId": null, + "name": "createdBy", + "objectMetadataUniversalIdentifier": Any, + "options": null, + "overrides": null, + "relationTargetFieldMetadataUniversalIdentifier": null, + "relationTargetObjectMetadataUniversalIdentifier": null, + "type": "ACTOR", + "universalIdentifier": Any, + "universalSettings": null, + "updatedAt": Any, + }, + { + "applicationUniversalIdentifier": Any, + "createdAt": Any, + "defaultValue": null, + "description": "Deletion date", + "icon": "IconCalendarClock", + "isActive": true, + "isLabelSyncedWithName": false, + "isNullable": true, + "isSystem": true, + "isSystemSideEffect": true, + "isUIEditable": false, + "isUnique": false, + "label": "Deleted at", + "morphId": null, + "name": "deletedAt", + "objectMetadataUniversalIdentifier": Any, + "options": null, + "overrides": null, + "relationTargetFieldMetadataUniversalIdentifier": null, + "relationTargetObjectMetadataUniversalIdentifier": null, + "type": "DATE_TIME", + "universalIdentifier": Any, + "universalSettings": null, + "updatedAt": Any, + }, + { + "applicationUniversalIdentifier": Any, + "createdAt": Any, + "defaultValue": 0, + "description": "Position", + "icon": "IconHierarchy2", + "isActive": true, + "isLabelSyncedWithName": false, + "isNullable": false, + "isSystem": true, + "isSystemSideEffect": true, + "isUIEditable": false, + "isUnique": false, + "label": "Position", + "morphId": null, + "name": "position", + "objectMetadataUniversalIdentifier": Any, + "options": null, + "overrides": null, + "relationTargetFieldMetadataUniversalIdentifier": null, + "relationTargetObjectMetadataUniversalIdentifier": null, + "type": "POSITION", + "universalIdentifier": Any, + "universalSettings": null, + "updatedAt": Any, + }, + { + "applicationUniversalIdentifier": Any, + "createdAt": Any, + "defaultValue": "now", + "description": "Last time the record was changed", + "icon": "IconCalendarClock", + "isActive": true, + "isLabelSyncedWithName": false, + "isNullable": false, + "isSystem": true, + "isSystemSideEffect": true, + "isUIEditable": false, + "isUnique": false, + "label": "Last update", + "morphId": null, + "name": "updatedAt", + "objectMetadataUniversalIdentifier": Any, + "options": null, + "overrides": null, + "relationTargetFieldMetadataUniversalIdentifier": null, + "relationTargetObjectMetadataUniversalIdentifier": null, + "type": "DATE_TIME", + "universalIdentifier": Any, + "universalSettings": null, + "updatedAt": Any, + }, + { + "applicationUniversalIdentifier": Any, + "createdAt": Any, + "defaultValue": { + "name": "''", + "source": "'MANUAL'", + }, + "description": "The workspace member who last updated the record", + "icon": "IconUserCircle", + "isActive": true, + "isLabelSyncedWithName": false, + "isNullable": false, + "isSystem": true, + "isSystemSideEffect": true, + "isUIEditable": false, + "isUnique": false, + "label": "Updated by", + "morphId": null, + "name": "updatedBy", + "objectMetadataUniversalIdentifier": Any, + "options": null, + "overrides": null, + "relationTargetFieldMetadataUniversalIdentifier": null, + "relationTargetObjectMetadataUniversalIdentifier": null, + "type": "ACTOR", + "universalIdentifier": Any, + "universalSettings": null, + "updatedAt": Any, + }, + { + "applicationUniversalIdentifier": Any, + "createdAt": Any, + "defaultValue": null, + "description": "Search vector", + "icon": "IconSearch", + "isActive": true, + "isLabelSyncedWithName": false, + "isNullable": true, + "isSystem": true, + "isSystemSideEffect": true, + "isUIEditable": false, + "isUnique": false, + "label": "Search vector", + "morphId": null, + "name": "searchVector", + "objectMetadataUniversalIdentifier": Any, + "options": null, + "overrides": null, + "relationTargetFieldMetadataUniversalIdentifier": null, + "relationTargetObjectMetadataUniversalIdentifier": null, + "type": "TS_VECTOR", + "universalIdentifier": Any, + "universalSettings": null, + "updatedAt": Any, + }, ], }, + { + "fieldIdByUniversalIdentifier": Any, + "flatEntity": { + "applicationUniversalIdentifier": Any, + "createdAt": Any, + "defaultValue": null, + "description": "Tickets tied to the Timeline Activity", + "icon": "IconTimelineEvent", + "isActive": true, + "isLabelSyncedWithName": false, + "isNullable": true, + "isSystem": false, + "isSystemSideEffect": true, + "isUIEditable": true, + "isUnique": false, + "label": "Timeline Activities", + "morphId": null, + "name": "timelineActivities", + "objectMetadataUniversalIdentifier": Any, + "options": null, + "overrides": null, + "relationTargetFieldMetadataUniversalIdentifier": Any, + "relationTargetObjectMetadataUniversalIdentifier": Any, + "type": "RELATION", + "universalIdentifier": Any, + "universalSettings": { + "relationType": "ONE_TO_MANY", + }, + "updatedAt": Any, + }, + "id": Any, + "metadataName": "fieldMetadata", + "relatedFieldId": Any, + "relatedUniversalFlatFieldMetadata": { + "applicationUniversalIdentifier": Any, + "createdAt": Any, + "defaultValue": null, + "description": "TimelineActivities Ticket", + "icon": "IconTimelineEvent", + "isActive": true, + "isLabelSyncedWithName": false, + "isNullable": true, + "isSystem": false, + "isSystemSideEffect": true, + "isUIEditable": true, + "isUnique": false, + "label": "Ticket", + "morphId": Any, + "name": "targetTicket", + "objectMetadataUniversalIdentifier": Any, + "options": null, + "overrides": null, + "relationTargetFieldMetadataUniversalIdentifier": Any, + "relationTargetObjectMetadataUniversalIdentifier": Any, + "type": "MORPH_RELATION", + "universalIdentifier": Any, + "universalSettings": { + "joinColumnName": "targetTicketId", + "onDelete": "SET_NULL", + "relationType": "MANY_TO_ONE", + }, + "updatedAt": Any, + }, + "type": "create", + }, + { + "fieldIdByUniversalIdentifier": Any, + "flatEntity": { + "applicationUniversalIdentifier": Any, + "createdAt": Any, + "defaultValue": null, + "description": "Tickets tied to the Attachment", + "icon": "IconFileImport", + "isActive": true, + "isLabelSyncedWithName": false, + "isNullable": true, + "isSystem": false, + "isSystemSideEffect": true, + "isUIEditable": true, + "isUnique": false, + "label": "Attachments", + "morphId": null, + "name": "attachments", + "objectMetadataUniversalIdentifier": Any, + "options": null, + "overrides": null, + "relationTargetFieldMetadataUniversalIdentifier": Any, + "relationTargetObjectMetadataUniversalIdentifier": Any, + "type": "RELATION", + "universalIdentifier": Any, + "universalSettings": { + "relationType": "ONE_TO_MANY", + }, + "updatedAt": Any, + }, + "id": Any, + "metadataName": "fieldMetadata", + "relatedFieldId": Any, + "relatedUniversalFlatFieldMetadata": { + "applicationUniversalIdentifier": Any, + "createdAt": Any, + "defaultValue": null, + "description": "Attachments Ticket", + "icon": "IconFileImport", + "isActive": true, + "isLabelSyncedWithName": false, + "isNullable": true, + "isSystem": false, + "isSystemSideEffect": true, + "isUIEditable": true, + "isUnique": false, + "label": "Ticket", + "morphId": Any, + "name": "targetTicket", + "objectMetadataUniversalIdentifier": Any, + "options": null, + "overrides": null, + "relationTargetFieldMetadataUniversalIdentifier": Any, + "relationTargetObjectMetadataUniversalIdentifier": Any, + "type": "MORPH_RELATION", + "universalIdentifier": Any, + "universalSettings": { + "joinColumnName": "targetTicketId", + "onDelete": "SET_NULL", + "relationType": "MANY_TO_ONE", + }, + "updatedAt": Any, + }, + "type": "create", + }, + { + "fieldIdByUniversalIdentifier": Any, + "flatEntity": { + "applicationUniversalIdentifier": Any, + "createdAt": Any, + "defaultValue": null, + "description": "Tickets tied to the Note Target", + "icon": "IconNotes", + "isActive": true, + "isLabelSyncedWithName": false, + "isNullable": true, + "isSystem": false, + "isSystemSideEffect": true, + "isUIEditable": true, + "isUnique": false, + "label": "Notes", + "morphId": null, + "name": "noteTargets", + "objectMetadataUniversalIdentifier": Any, + "options": null, + "overrides": null, + "relationTargetFieldMetadataUniversalIdentifier": Any, + "relationTargetObjectMetadataUniversalIdentifier": Any, + "type": "RELATION", + "universalIdentifier": Any, + "universalSettings": { + "relationType": "ONE_TO_MANY", + }, + "updatedAt": Any, + }, + "id": Any, + "metadataName": "fieldMetadata", + "relatedFieldId": Any, + "relatedUniversalFlatFieldMetadata": { + "applicationUniversalIdentifier": Any, + "createdAt": Any, + "defaultValue": null, + "description": "NoteTargets Ticket", + "icon": "IconCheckbox", + "isActive": true, + "isLabelSyncedWithName": false, + "isNullable": true, + "isSystem": false, + "isSystemSideEffect": true, + "isUIEditable": true, + "isUnique": false, + "label": "Ticket", + "morphId": Any, + "name": "targetTicket", + "objectMetadataUniversalIdentifier": Any, + "options": null, + "overrides": null, + "relationTargetFieldMetadataUniversalIdentifier": Any, + "relationTargetObjectMetadataUniversalIdentifier": Any, + "type": "MORPH_RELATION", + "universalIdentifier": Any, + "universalSettings": { + "joinColumnName": "targetTicketId", + "onDelete": "SET_NULL", + "relationType": "MANY_TO_ONE", + }, + "updatedAt": Any, + }, + "type": "create", + }, + { + "fieldIdByUniversalIdentifier": Any, + "flatEntity": { + "applicationUniversalIdentifier": Any, + "createdAt": Any, + "defaultValue": null, + "description": "Tickets tied to the Task Target", + "icon": "IconCheckbox", + "isActive": true, + "isLabelSyncedWithName": false, + "isNullable": true, + "isSystem": false, + "isSystemSideEffect": true, + "isUIEditable": true, + "isUnique": false, + "label": "Tasks", + "morphId": null, + "name": "taskTargets", + "objectMetadataUniversalIdentifier": Any, + "options": null, + "overrides": null, + "relationTargetFieldMetadataUniversalIdentifier": Any, + "relationTargetObjectMetadataUniversalIdentifier": Any, + "type": "RELATION", + "universalIdentifier": Any, + "universalSettings": { + "relationType": "ONE_TO_MANY", + }, + "updatedAt": Any, + }, + "id": Any, + "metadataName": "fieldMetadata", + "relatedFieldId": Any, + "relatedUniversalFlatFieldMetadata": { + "applicationUniversalIdentifier": Any, + "createdAt": Any, + "defaultValue": null, + "description": "TaskTargets Ticket", + "icon": "IconCheckbox", + "isActive": true, + "isLabelSyncedWithName": false, + "isNullable": true, + "isSystem": false, + "isSystemSideEffect": true, + "isUIEditable": true, + "isUnique": false, + "label": "Ticket", + "morphId": Any, + "name": "targetTicket", + "objectMetadataUniversalIdentifier": Any, + "options": null, + "overrides": null, + "relationTargetFieldMetadataUniversalIdentifier": Any, + "relationTargetObjectMetadataUniversalIdentifier": Any, + "type": "MORPH_RELATION", + "universalIdentifier": Any, + "universalSettings": { + "joinColumnName": "targetTicketId", + "onDelete": "SET_NULL", + "relationType": "MANY_TO_ONE", + }, + "updatedAt": Any, + }, + "type": "create", + }, + { + "flatEntity": { + "applicationUniversalIdentifier": Any, + "createdAt": Any, + "indexType": "BTREE", + "indexWhereClause": null, + "isCustom": true, + "isSystemSideEffect": true, + "isUnique": false, + "name": "IDX_8da02734dc044074a6a2c224f2a", + "objectMetadataUniversalIdentifier": Any, + "universalFlatIndexFieldMetadatas": [ + { + "createdAt": Any, + "fieldMetadataUniversalIdentifier": Any, + "indexMetadataUniversalIdentifier": Any, + "order": 0, + "subFieldName": null, + "updatedAt": Any, + }, + ], + "universalIdentifier": Any, + "updatedAt": Any, + }, + "metadataName": "index", + "type": "create", + }, + { + "flatEntity": { + "applicationUniversalIdentifier": Any, + "createdAt": Any, + "indexType": "BTREE", + "indexWhereClause": null, + "isCustom": true, + "isSystemSideEffect": true, + "isUnique": false, + "name": "IDX_890a5ffa99e9b995803d50f2298", + "objectMetadataUniversalIdentifier": Any, + "universalFlatIndexFieldMetadatas": [ + { + "createdAt": Any, + "fieldMetadataUniversalIdentifier": Any, + "indexMetadataUniversalIdentifier": Any, + "order": 0, + "subFieldName": null, + "updatedAt": Any, + }, + ], + "universalIdentifier": Any, + "updatedAt": Any, + }, + "metadataName": "index", + "type": "create", + }, + { + "flatEntity": { + "applicationUniversalIdentifier": Any, + "createdAt": Any, + "indexType": "BTREE", + "indexWhereClause": null, + "isCustom": true, + "isSystemSideEffect": true, + "isUnique": false, + "name": "IDX_d2022a25ca76206ddf16d88efd5", + "objectMetadataUniversalIdentifier": Any, + "universalFlatIndexFieldMetadatas": [ + { + "createdAt": Any, + "fieldMetadataUniversalIdentifier": Any, + "indexMetadataUniversalIdentifier": Any, + "order": 0, + "subFieldName": null, + "updatedAt": Any, + }, + ], + "universalIdentifier": Any, + "updatedAt": Any, + }, + "metadataName": "index", + "type": "create", + }, + { + "flatEntity": { + "applicationUniversalIdentifier": Any, + "createdAt": Any, + "indexType": "BTREE", + "indexWhereClause": null, + "isCustom": true, + "isSystemSideEffect": true, + "isUnique": false, + "name": "IDX_7d7a4e9e0805030aee982fdfa31", + "objectMetadataUniversalIdentifier": Any, + "universalFlatIndexFieldMetadatas": [ + { + "createdAt": Any, + "fieldMetadataUniversalIdentifier": Any, + "indexMetadataUniversalIdentifier": Any, + "order": 0, + "subFieldName": null, + "updatedAt": Any, + }, + ], + "universalIdentifier": Any, + "updatedAt": Any, + }, + "metadataName": "index", + "type": "create", + }, { "flatEntity": { "applicationUniversalIdentifier": Any, diff --git a/packages/twenty-server/test/integration/metadata/suites/application/failing-sync-application-default-relation-name-collision.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/application/failing-sync-application-default-relation-name-collision.integration-spec.ts new file mode 100644 index 0000000000..97aa0e48c4 --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/application/failing-sync-application-default-relation-name-collision.integration-spec.ts @@ -0,0 +1,123 @@ +import { expectOneNotInternalServerErrorSnapshot } from 'test/integration/graphql/utils/expect-one-not-internal-server-error-snapshot.util'; +import { buildBaseManifest } from 'test/integration/metadata/suites/application/utils/build-base-manifest.util'; +import { buildDefaultObjectManifest } from 'test/integration/metadata/suites/application/utils/build-default-object-manifest.util'; +import { cleanupApplicationAndAppRegistration } from 'test/integration/metadata/suites/application/utils/cleanup-application-and-app-registration.util'; +import { setupApplicationForSync } from 'test/integration/metadata/suites/application/utils/setup-application-for-sync.util'; +import { syncApplication } from 'test/integration/metadata/suites/application/utils/sync-application.util'; +import { + getSystemRelationFieldUniversalIdentifier, + type ObjectManifest, +} from 'twenty-shared/application'; +import { STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS } from 'twenty-shared/metadata'; +import { FieldMetadataType } from 'twenty-shared/types'; +import { v4 as uuidv4 } from 'uuid'; + +const TEST_APP_ID = uuidv4(); +const TEST_ROLE_ID = uuidv4(); +const OBJECT_UNIVERSAL_IDENTIFIER = uuidv4(); + +const OBJECT_NAME_SINGULAR = 'defaultRelationCollisionTarget'; +const OBJECT_NAME_PLURAL = 'defaultRelationCollisionTargets'; + +// The engine forward field of the attachment default relation derives its +// universal identifier name-free from the host and relation target object +// identifiers, so a manifest field can only squat it by pinning that exact +// derived value. +const DERIVED_ATTACHMENTS_UNIVERSAL_IDENTIFIER = + getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: TEST_APP_ID, + objectUniversalIdentifier: OBJECT_UNIVERSAL_IDENTIFIER, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.attachment, + }); + +const buildObjectWithAttachmentsField = ({ + attachmentsFieldUniversalIdentifier, +}: { + attachmentsFieldUniversalIdentifier: string; +}): ObjectManifest => + buildDefaultObjectManifest({ + applicationUniversalIdentifier: TEST_APP_ID, + nameSingular: OBJECT_NAME_SINGULAR, + namePlural: OBJECT_NAME_PLURAL, + labelSingular: 'Default Relation Collision Target', + labelPlural: 'Default Relation Collision Targets', + description: 'Object used to test default relation name collisions', + universalIdentifier: OBJECT_UNIVERSAL_IDENTIFIER, + additionalFields: [ + { + universalIdentifier: attachmentsFieldUniversalIdentifier, + type: FieldMetadataType.TEXT, + name: 'attachments', + label: 'Caller Attachments', + isNullable: true, + }, + ], + }); + +// The default relations are engine-owned: the create side-effect handler +// always emits them, so a manifest field can never take their place. It either +// collides on name (random identifier) or on universal identifier (derived +// identifier), and both collisions hard-fail the sync. +describe('Sync application default relation name collision', () => { + beforeAll(async () => { + await setupApplicationForSync({ + applicationUniversalIdentifier: TEST_APP_ID, + name: 'Test Default Relation Name Collision App', + description: 'App for testing default relation name collisions', + sourcePath: 'test-default-relation-name-collision', + }); + }, 60000); + + afterAll(async () => { + await cleanupApplicationAndAppRegistration({ + applicationUniversalIdentifier: TEST_APP_ID, + }); + }); + + it('should reject a manifest field named after a default relation when it carries a pinned universal identifier', async () => { + // The pinned identifier differs from the derived forward field identifier, + // so the side-effect "attachments" RELATION field and the manifest TEXT + // field coexist in the create bucket and collide on name in the validator. + const { errors } = await syncApplication({ + manifest: buildBaseManifest({ + appId: TEST_APP_ID, + roleId: TEST_ROLE_ID, + overrides: { + objects: [ + buildObjectWithAttachmentsField({ + attachmentsFieldUniversalIdentifier: uuidv4(), + }), + ], + }, + }), + expectToFail: true, + }); + + expectOneNotInternalServerErrorSnapshot({ errors }); + }, 60000); + + it('should reject a manifest field named after a default relation when it carries the derived universal identifier', async () => { + // With the derived identifier the manifest field collides with the + // side-effect forward field on universal identifier: the engine merge + // detects a caller entity squatting a system-reserved identifier and + // hard-fails (RESERVED_SYSTEM_UNIVERSAL_IDENTIFIER). + const { errors } = await syncApplication({ + manifest: buildBaseManifest({ + appId: TEST_APP_ID, + roleId: TEST_ROLE_ID, + overrides: { + objects: [ + buildObjectWithAttachmentsField({ + attachmentsFieldUniversalIdentifier: + DERIVED_ATTACHMENTS_UNIVERSAL_IDENTIFIER, + }), + ], + }, + }), + expectToFail: true, + }); + + expectOneNotInternalServerErrorSnapshot({ errors }); + }, 60000); +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/application/failing-sync-application-search-vector-identifier-hijack.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/application/failing-sync-application-search-vector-identifier-hijack.integration-spec.ts new file mode 100644 index 0000000000..7305e1c27e --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/application/failing-sync-application-search-vector-identifier-hijack.integration-spec.ts @@ -0,0 +1,167 @@ +import { expectOneNotInternalServerErrorSnapshot } from 'test/integration/graphql/utils/expect-one-not-internal-server-error-snapshot.util'; +import { buildBaseManifest } from 'test/integration/metadata/suites/application/utils/build-base-manifest.util'; +import { buildDefaultObjectManifest } from 'test/integration/metadata/suites/application/utils/build-default-object-manifest.util'; +import { cleanupApplicationAndAppRegistration } from 'test/integration/metadata/suites/application/utils/cleanup-application-and-app-registration.util'; +import { setupApplicationForSync } from 'test/integration/metadata/suites/application/utils/setup-application-for-sync.util'; +import { syncApplication } from 'test/integration/metadata/suites/application/utils/sync-application.util'; +import { + getFieldUniversalIdentifier, + type ObjectManifest, +} from 'twenty-shared/application'; +import { FieldMetadataType } from 'twenty-shared/types'; +import { v4 as uuidv4 } from 'uuid'; + +const TEST_APP_ID = uuidv4(); +const TEST_ROLE_ID = uuidv4(); +const OBJECT_UNIVERSAL_IDENTIFIER = uuidv4(); + +const OBJECT_NAME_SINGULAR = 'searchVectorHijackTarget'; +const OBJECT_NAME_PLURAL = 'searchVectorHijackTargets'; + +const SEARCH_VECTOR_UNIVERSAL_IDENTIFIER = getFieldUniversalIdentifier({ + applicationUniversalIdentifier: TEST_APP_ID, + objectUniversalIdentifier: OBJECT_UNIVERSAL_IDENTIFIER, + name: 'searchVector', +}); + +const buildCleanObject = (): ObjectManifest => + buildDefaultObjectManifest({ + applicationUniversalIdentifier: TEST_APP_ID, + nameSingular: OBJECT_NAME_SINGULAR, + namePlural: OBJECT_NAME_PLURAL, + labelSingular: 'Search Vector Hijack Target', + labelPlural: 'Search Vector Hijack Targets', + description: 'Object used to test search vector identifier hijack', + universalIdentifier: OBJECT_UNIVERSAL_IDENTIFIER, + }); + +// Squat the engine-owned searchVector slot: declare a foreign, non-system +// field carrying the deterministic universal identifier the engine derives +// for searchVector. This is the exact override a malicious manifest could +// attempt on a second sync to hijack a system field. +const buildObjectHijackingSearchVectorUniversalIdentifier = + (): ObjectManifest => { + const cleanObject = buildCleanObject(); + + return { + ...cleanObject, + fields: [ + ...cleanObject.fields, + { + universalIdentifier: SEARCH_VECTOR_UNIVERSAL_IDENTIFIER, + type: FieldMetadataType.TEXT, + name: 'stolenSearchVector', + label: 'Stolen Search Vector', + isNullable: true, + }, + ], + }; + }; + +// Redeclare searchVector under its derived universal identifier: the builder +// matches the engine-provisioned field and emits an update, rejected because +// the manifest defaults mutate properties system fields do not allow updating. +const buildObjectAuthoringSearchVector = (): ObjectManifest => { + const cleanObject = buildCleanObject(); + + return { + ...cleanObject, + fields: [ + ...cleanObject.fields, + { + universalIdentifier: SEARCH_VECTOR_UNIVERSAL_IDENTIFIER, + type: FieldMetadataType.TS_VECTOR, + name: 'searchVector', + label: 'Search vector', + }, + ], + }; +}; + +// Declare a field reusing the reserved searchVector name under a foreign +// universal identifier: the builder emits a create colliding with the +// engine-provisioned field name. +const buildObjectCreatingForeignSearchVector = (): ObjectManifest => { + const cleanObject = buildCleanObject(); + + return { + ...cleanObject, + fields: [ + ...cleanObject.fields, + { + universalIdentifier: uuidv4(), + type: FieldMetadataType.TEXT, + name: 'searchVector', + label: 'Foreign Search Vector', + isNullable: true, + }, + ], + }; +}; + +describe('Sync application should reject hijacking the searchVector system field', () => { + beforeAll(async () => { + await setupApplicationForSync({ + applicationUniversalIdentifier: TEST_APP_ID, + name: 'Test Search Vector Hijack App', + description: 'App for testing search vector identifier hijack', + sourcePath: 'test-search-vector-hijack', + }); + + await syncApplication({ + expectToFail: false, + manifest: buildBaseManifest({ + appId: TEST_APP_ID, + roleId: TEST_ROLE_ID, + overrides: { objects: [buildCleanObject()] }, + }), + }); + }, 60000); + + afterAll(async () => { + await cleanupApplicationAndAppRegistration({ + applicationUniversalIdentifier: TEST_APP_ID, + }); + }); + + it('should reject a manifest field declared under the reserved searchVector name', async () => { + const { errors } = await syncApplication({ + manifest: buildBaseManifest({ + appId: TEST_APP_ID, + roleId: TEST_ROLE_ID, + overrides: { objects: [buildObjectAuthoringSearchVector()] }, + }), + expectToFail: true, + }); + + expectOneNotInternalServerErrorSnapshot({ errors }); + }, 60000); + + it('should reject a manifest field reusing the searchVector name under a foreign universal identifier', async () => { + const { errors } = await syncApplication({ + manifest: buildBaseManifest({ + appId: TEST_APP_ID, + roleId: TEST_ROLE_ID, + overrides: { objects: [buildObjectCreatingForeignSearchVector()] }, + }), + expectToFail: true, + }); + + expectOneNotInternalServerErrorSnapshot({ errors }); + }, 60000); + + it('should reject a manifest field squatting the searchVector deterministic universal identifier', async () => { + const { errors } = await syncApplication({ + manifest: buildBaseManifest({ + appId: TEST_APP_ID, + roleId: TEST_ROLE_ID, + overrides: { + objects: [buildObjectHijackingSearchVectorUniversalIdentifier()], + }, + }), + expectToFail: true, + }); + + expectOneNotInternalServerErrorSnapshot({ errors }); + }, 60000); +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/application/successful-sync-application-workspace-migration.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/application/successful-sync-application-workspace-migration.integration-spec.ts index e35ec1340b..0bd3fa62aa 100644 --- a/packages/twenty-server/test/integration/metadata/suites/application/successful-sync-application-workspace-migration.integration-spec.ts +++ b/packages/twenty-server/test/integration/metadata/suites/application/successful-sync-application-workspace-migration.integration-spec.ts @@ -8,8 +8,15 @@ import { findManyObjectMetadata } from 'test/integration/metadata/suites/object- import { findRoles } from 'test/integration/metadata/suites/role/utils/find-roles.util'; import { findSkills } from 'test/integration/metadata/suites/skill/utils/find-skills.util'; import { extractRecordIdsAndDatesAsExpectAny } from 'test/utils/extract-record-ids-and-dates-as-expect-any'; -import { type FieldManifest, type Manifest } from 'twenty-shared/application'; -import { STANDARD_OBJECTS } from 'twenty-shared/metadata'; +import { + type FieldManifest, + getSystemRelationFieldUniversalIdentifier, + type Manifest, +} from 'twenty-shared/application'; +import { + DEFAULT_RELATIONS_OBJECTS_STANDARD_IDS, + STANDARD_OBJECTS, +} from 'twenty-shared/metadata'; import { FieldMetadataType } from 'twenty-shared/types'; import { v4 as uuidv4 } from 'uuid'; @@ -168,6 +175,147 @@ describe('syncApplication', () => { }); }, 60000); + // The snapshot masks every identifier as Any, so it cannot catch a + // regression in the deterministic universal identifiers of the default + // relations. This test recomputes them and asserts the emitted migration + // actions carry them exactly, on both sides of each relation. + it('should provision the four engine-owned default relations on initial sync', async () => { + const NAME_PLURAL_BY_STANDARD_OBJECT_NAME_SINGULAR = { + timelineActivity: 'timelineActivities', + attachment: 'attachments', + noteTarget: 'noteTargets', + taskTarget: 'taskTargets', + } as const; + + const { data: syncData } = await syncApplication({ + manifest: buildManifest(), + expectToFail: false, + }); + + type FlatFieldEntity = { + universalIdentifier: string; + name?: string; + type?: string; + isSystem?: boolean; + isSystemSideEffect?: boolean; + morphId?: string | null; + objectMetadataUniversalIdentifier?: string; + relationTargetObjectMetadataUniversalIdentifier?: string | null; + relationTargetFieldMetadataUniversalIdentifier?: string | null; + universalSettings?: { joinColumnName?: string } | null; + universalFlatIndexFieldMetadatas?: { + fieldMetadataUniversalIdentifier: string; + }[]; + }; + type FlatEntityAction = { + type: string; + metadataName: string; + flatEntity: FlatFieldEntity; + // A relation field create action carries its reverse field inline. + relatedUniversalFlatFieldMetadata?: FlatFieldEntity; + }; + + const actions = ( + syncData as { + syncApplication: { actions: FlatEntityAction[] }; + } + ).syncApplication.actions; + + const fieldCreateActionByUniversalIdentifier = Object.fromEntries( + actions + .filter( + (action) => + action.metadataName === 'fieldMetadata' && action.type === 'create', + ) + .map((action) => [action.flatEntity.universalIdentifier, action]), + ); + const indexCreateActions = actions.filter( + (action) => action.metadataName === 'index' && action.type === 'create', + ); + + for (const standardObjectNameSingular of DEFAULT_RELATIONS_OBJECTS_STANDARD_IDS) { + const standardObjectUniversalIdentifier = + STANDARD_OBJECTS[standardObjectNameSingular].universalIdentifier; + const standardObjectNamePlural = + NAME_PLURAL_BY_STANDARD_OBJECT_NAME_SINGULAR[ + standardObjectNameSingular + ]; + + const forwardUniversalIdentifier = + getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: TEST_APP_ID, + objectUniversalIdentifier: TEST_OBJECT.universalIdentifier, + relationTargetObjectUniversalIdentifier: + standardObjectUniversalIdentifier, + }); + const reverseUniversalIdentifier = + getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: TEST_APP_ID, + objectUniversalIdentifier: standardObjectUniversalIdentifier, + relationTargetObjectUniversalIdentifier: + TEST_OBJECT.universalIdentifier, + }); + + // Forward RELATION field on the ticket, named after the standard + // object's namePlural, under the name-free universal identifier. + const forwardCreateAction = + fieldCreateActionByUniversalIdentifier[forwardUniversalIdentifier]; + + expect(forwardCreateAction?.flatEntity).toMatchObject({ + name: standardObjectNamePlural, + type: FieldMetadataType.RELATION, + // Engine-owned (isSystemSideEffect) but not isSystem: the engine + // rename/delete cascades must pass the validator system-field gates. + isSystem: false, + isSystemSideEffect: true, + objectMetadataUniversalIdentifier: TEST_OBJECT.universalIdentifier, + relationTargetObjectMetadataUniversalIdentifier: + standardObjectUniversalIdentifier, + relationTargetFieldMetadataUniversalIdentifier: + reverseUniversalIdentifier, + }); + + // Reverse MORPH_RELATION field on the standard object, carried inline on + // the forward create action, under the name-free deterministic universal + // identifier, with the engine-pinned targetMorphId and the join column. + expect( + forwardCreateAction?.relatedUniversalFlatFieldMetadata, + ).toMatchObject({ + name: 'targetTicket', + type: FieldMetadataType.MORPH_RELATION, + isSystem: false, + isSystemSideEffect: true, + universalIdentifier: reverseUniversalIdentifier, + morphId: + STANDARD_OBJECTS[standardObjectNameSingular].morphIds.targetMorphId + .morphId, + objectMetadataUniversalIdentifier: standardObjectUniversalIdentifier, + relationTargetObjectMetadataUniversalIdentifier: + TEST_OBJECT.universalIdentifier, + relationTargetFieldMetadataUniversalIdentifier: + forwardUniversalIdentifier, + universalSettings: expect.objectContaining({ + joinColumnName: 'targetTicketId', + }), + }); + + // Engine-owned join-column index backing the reverse field. + const reverseJoinColumnIndexAction = indexCreateActions.find((action) => + action.flatEntity.universalFlatIndexFieldMetadatas?.some( + (indexFieldMetadata) => + indexFieldMetadata.fieldMetadataUniversalIdentifier === + reverseUniversalIdentifier, + ), + ); + + expect(reverseJoinColumnIndexAction).toBeDefined(); + expect(reverseJoinColumnIndexAction?.flatEntity).toMatchObject({ + isSystemSideEffect: true, + objectMetadataUniversalIdentifier: standardObjectUniversalIdentifier, + }); + } + }, 60000); + it('should delete old field and create equivalent one when field universalIdentifier changes', async () => { const originalFieldId = uuidv4(); const updatedFieldId = uuidv4(); diff --git a/packages/twenty-server/test/integration/metadata/suites/application/utils/build-default-object-manifest.util.ts b/packages/twenty-server/test/integration/metadata/suites/application/utils/build-default-object-manifest.util.ts index 9860c56156..26e59f2b40 100644 --- a/packages/twenty-server/test/integration/metadata/suites/application/utils/build-default-object-manifest.util.ts +++ b/packages/twenty-server/test/integration/metadata/suites/application/utils/build-default-object-manifest.util.ts @@ -2,11 +2,12 @@ import { getFieldUniversalIdentifier, type ObjectManifest, } from 'twenty-shared/application'; -import { FieldMetadataType } from 'twenty-shared/types'; import { v4 as uuidv4 } from 'uuid'; -// System field universal identifiers are server-owned and validated against -// the deterministic derivation, so the manifest must carry the derived values. +// System fields (id, createdAt, …) are engine-provisioned and must not be +// manifest-authored: the sync rejects any reserved field name. The label +// identifier still defaults to the engine-derived id field universal +// identifier, which is resolvable without declaring the field. export const buildDefaultObjectManifest = ({ nameSingular, namePlural, @@ -32,15 +33,11 @@ export const buildDefaultObjectManifest = ({ }): ObjectManifest => { const objectUniversalIdentifier = universalIdentifier ?? uuidv4(); - const computeSystemFieldUniversalIdentifier = (fieldName: string) => - getFieldUniversalIdentifier({ - applicationUniversalIdentifier, - objectUniversalIdentifier, - name: fieldName, - }); - - const idFieldUniversalIdentifier = - computeSystemFieldUniversalIdentifier('id'); + const idFieldUniversalIdentifier = getFieldUniversalIdentifier({ + applicationUniversalIdentifier, + objectUniversalIdentifier, + name: 'id', + }); return { universalIdentifier: objectUniversalIdentifier, @@ -53,57 +50,6 @@ export const buildDefaultObjectManifest = ({ labelPlural, description, icon, - fields: [ - { - universalIdentifier: idFieldUniversalIdentifier, - type: FieldMetadataType.UUID, - name: 'id', - label: 'Id', - }, - { - universalIdentifier: computeSystemFieldUniversalIdentifier('createdAt'), - type: FieldMetadataType.DATE_TIME, - name: 'createdAt', - label: 'Creation date', - }, - { - universalIdentifier: computeSystemFieldUniversalIdentifier('updatedAt'), - type: FieldMetadataType.DATE_TIME, - name: 'updatedAt', - label: 'Last update', - }, - { - universalIdentifier: computeSystemFieldUniversalIdentifier('deletedAt'), - type: FieldMetadataType.DATE_TIME, - name: 'deletedAt', - label: 'Deleted at', - }, - { - universalIdentifier: computeSystemFieldUniversalIdentifier('createdBy'), - type: FieldMetadataType.ACTOR, - name: 'createdBy', - label: 'Created by', - }, - { - universalIdentifier: computeSystemFieldUniversalIdentifier('updatedBy'), - type: FieldMetadataType.ACTOR, - name: 'updatedBy', - label: 'Updated by', - }, - { - universalIdentifier: computeSystemFieldUniversalIdentifier('position'), - type: FieldMetadataType.POSITION, - name: 'position', - label: 'Position', - }, - { - universalIdentifier: - computeSystemFieldUniversalIdentifier('searchVector'), - type: FieldMetadataType.TS_VECTOR, - name: 'searchVector', - label: 'Search vector', - }, - ...additionalFields, - ], + fields: [...additionalFields], }; }; diff --git a/packages/twenty-server/test/integration/metadata/suites/object-metadata/create-one-object-metadata-deterministic-field-universal-identifiers.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/object-metadata/create-one-object-metadata-deterministic-field-universal-identifiers.integration-spec.ts index 6a2145c7f8..dc1b239218 100644 --- a/packages/twenty-server/test/integration/metadata/suites/object-metadata/create-one-object-metadata-deterministic-field-universal-identifiers.integration-spec.ts +++ b/packages/twenty-server/test/integration/metadata/suites/object-metadata/create-one-object-metadata-deterministic-field-universal-identifiers.integration-spec.ts @@ -3,7 +3,10 @@ import { findManyFieldsMetadata } from 'test/integration/metadata/suites/field-m import { createOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata.util'; import { deleteOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util'; -import { getFieldUniversalIdentifier } from 'twenty-shared/application'; +import { + getFieldUniversalIdentifier, + getSystemRelationFieldUniversalIdentifier, +} from 'twenty-shared/application'; import { capitalize, isDefined } from 'twenty-shared/utils'; const SYSTEM_FIELD_NAMES = [ @@ -140,13 +143,6 @@ describe('Custom object creation deterministic field universal identifiers', () ); expect(forwardField).toBeDefined(); - expect(forwardField?.universalIdentifier).toBe( - getFieldUniversalIdentifier({ - applicationUniversalIdentifier, - objectUniversalIdentifier, - name: forwardFieldName, - }), - ); const standardRelationObject = forwardField?.relation?.targetObjectMetadata; @@ -174,11 +170,23 @@ describe('Custom object creation deterministic field universal identifiers', () .find((field: FetchedField) => field.name === storedReverseFieldName); expect(reverseField).toBeDefined(); - expect(reverseField?.universalIdentifier).toBe( - getFieldUniversalIdentifier({ + // Both sides of a system relation use the name-free deterministic + // identifier (host + relation target object, direction encoded by + // swapping them) so an object rename stays a lossless update. + expect(forwardField?.universalIdentifier).toBe( + getSystemRelationFieldUniversalIdentifier({ applicationUniversalIdentifier, - objectUniversalIdentifier: standardRelationObject.universalIdentifier, - name: storedReverseFieldName, + objectUniversalIdentifier: objectUniversalIdentifier, + relationTargetObjectUniversalIdentifier: + standardRelationObject.universalIdentifier, + }), + ); + expect(reverseField?.universalIdentifier).toBe( + getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier, + objectUniversalIdentifier: + standardRelationObject.universalIdentifier, + relationTargetObjectUniversalIdentifier: objectUniversalIdentifier, }), ); }, diff --git a/packages/twenty-server/test/integration/metadata/suites/object-metadata/rename-custom-object.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/object-metadata/rename-custom-object.integration-spec.ts index 9b865acbf2..c325dff383 100644 --- a/packages/twenty-server/test/integration/metadata/suites/object-metadata/rename-custom-object.integration-spec.ts +++ b/packages/twenty-server/test/integration/metadata/suites/object-metadata/rename-custom-object.integration-spec.ts @@ -1,4 +1,6 @@ +import { deleteOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/delete-one-field-metadata.util'; import { findManyFieldsMetadataQueryFactory } from 'test/integration/metadata/suites/field-metadata/utils/find-many-fields-metadata-query-factory.util'; +import { updateOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/update-one-field-metadata.util'; import { createOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata.util'; import { deleteOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util'; import { findManyObjectMetadataQueryFactory } from 'test/integration/metadata/suites/object-metadata/utils/find-many-object-metadata-query-factory.util'; @@ -25,6 +27,7 @@ describe('Custom object renaming', () => { objectMetadataId: '', foreignKeyFieldMetadataId: '', relationFieldMetadataId: '', + relationFieldMetadataUniversalIdentifier: '', }, }), {}, @@ -47,6 +50,7 @@ describe('Custom object renaming', () => { name label type + universalIdentifier object { id } @@ -132,10 +136,18 @@ describe('Custom object renaming', () => { const relationFieldMetadataId = relationFieldMetadata?.id; expect(relationFieldMetadataId).not.toBeUndefined(); + // Reverse system relation fields carry the engine-derived label + // (capitalized source object nameSingular) + expect(relationFieldMetadata?.label).toBe( + capitalize(CUSTOM_OBJECT.nameSingular), + ); // @ts-expect-error legacy noImplicitAny standardObjectRelationsMap[relation].relationFieldMetadataId = relationFieldMetadataId; + // @ts-expect-error legacy noImplicitAny + standardObjectRelationsMap[relation].relationFieldMetadataUniversalIdentifier = + relationFieldMetadata?.universalIdentifier; }); }); @@ -171,9 +183,95 @@ describe('Custom object renaming', () => { expect(data.updateOneObject.namePlural).toBe(HOUSE_NAME_PLURAL); expect(data.updateOneObject.labelSingular).toBe(HOUSE_LABEL_SINGULAR); expect(data.updateOneObject.labelPlural).toBe(HOUSE_LABEL_PLURAL); + + // The reverse morph fields on the standard objects must be renamed in place + // (name and engine-derived label follow the new object name) while keeping + // their universal identifier stable, so the rename stays lossless. + const expectedReverseFieldName = `target${capitalize(HOUSE_NAME_SINGULAR)}`; + const expectedReverseFieldLabel = capitalize(HOUSE_NAME_SINGULAR); + const fields = await makeMetadataAPIRequest(fieldsGraphqlOperation); + + STANDARD_OBJECT_RELATIONS.forEach((relation) => { + // @ts-expect-error legacy noImplicitAny + const relationEntry = standardObjectRelationsMap[relation]; + const relationFieldMetadataId = relationEntry.relationFieldMetadataId; + const relationFieldMetadataUniversalIdentifier = + relationEntry.relationFieldMetadataUniversalIdentifier; + + const renamedReverseField = fields.body.data.fields.edges + // @ts-expect-error legacy noImplicitAny + .map((field) => field.node) + // @ts-expect-error legacy noImplicitAny + .find((field) => field.id === relationFieldMetadataId); + + expect(renamedReverseField).toBeDefined(); + expect(renamedReverseField.name).toBe(expectedReverseFieldName); + expect(renamedReverseField.label).toBe(expectedReverseFieldLabel); + expect(renamedReverseField.universalIdentifier).toBe( + relationFieldMetadataUniversalIdentifier, + ); + }); }); - it('3. should delete custom object', async () => { + it('3. should reject direct deletion of a system side-effect relation field', async () => { + // @ts-expect-error legacy noImplicitAny + const timelineActivityRelation = standardObjectRelationsMap['timelineActivity']; + const relationFieldMetadataId = + timelineActivityRelation.relationFieldMetadataId; + + const { errors } = await deleteOneFieldMetadata({ + expectToFail: true, + input: { idToDelete: relationFieldMetadataId }, + }); + + expect(errors).toBeDefined(); + expect(errors.length).toBeGreaterThan(0); + }); + + it('4. should reject direct edition of a system side-effect relation field', async () => { + // @ts-expect-error legacy noImplicitAny + const timelineActivityRelation = standardObjectRelationsMap['timelineActivity']; + const relationFieldMetadataId = + timelineActivityRelation.relationFieldMetadataId; + + const { errors } = await updateOneFieldMetadata({ + expectToFail: true, + input: { + idToUpdate: relationFieldMetadataId, + updatePayload: { label: 'Should not be editable' }, + }, + }); + + expect(errors).toBeDefined(); + expect(errors.length).toBeGreaterThan(0); + }); + + it('5. should reject a morph relations update payload on a system side-effect relation field', async () => { + // @ts-expect-error legacy noImplicitAny + const timelineActivityRelation = standardObjectRelationsMap['timelineActivity']; + const relationFieldMetadataId = + timelineActivityRelation.relationFieldMetadataId; + + // morphRelationsUpdatePayload is not an editable property, so it must be + // rejected explicitly for engine-owned fields instead of silently creating + // relation fields and indexes on them + const { errors } = await updateOneFieldMetadata({ + expectToFail: true, + input: { + idToUpdate: relationFieldMetadataId, + updatePayload: { + morphRelationsUpdatePayload: [ + { targetObjectMetadataId: listingObjectId }, + ], + }, + }, + }); + + expect(errors).toBeDefined(); + expect(errors.length).toBeGreaterThan(0); + }); + + it('6. should delete custom object', async () => { await updateOneObjectMetadata({ expectToFail: false, input: { diff --git a/packages/twenty-shared/src/application/deterministic-identifier/__tests__/get-system-relation-field-universal-identifier.util.spec.ts b/packages/twenty-shared/src/application/deterministic-identifier/__tests__/get-system-relation-field-universal-identifier.util.spec.ts new file mode 100644 index 0000000000..31fc953322 --- /dev/null +++ b/packages/twenty-shared/src/application/deterministic-identifier/__tests__/get-system-relation-field-universal-identifier.util.spec.ts @@ -0,0 +1,52 @@ +import { getFieldUniversalIdentifier } from '@/application/deterministic-identifier/get-field-universal-identifier.util'; +import { getSystemRelationFieldUniversalIdentifier } from '@/application/deterministic-identifier/get-system-relation-field-universal-identifier.util'; + +const APP = '11111111-1111-4111-8111-111111111111'; +const HOST_OBJECT = '22222222-2222-4222-8222-222222222222'; +const RELATION_TARGET_OBJECT = '33333333-3333-4333-8333-333333333333'; + +describe('getSystemRelationFieldUniversalIdentifier', () => { + it('derives a deterministic id from the host and relation target object identifiers', () => { + expect( + getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: APP, + objectUniversalIdentifier: HOST_OBJECT, + relationTargetObjectUniversalIdentifier: RELATION_TARGET_OBJECT, + }), + ).toBe('d3a641a1-f0fd-5213-a28e-af81e00c991e'); + }); + + it('derives distinct ids for the two sides of the same relation (host and target swapped)', () => { + const reverseSide = getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: APP, + objectUniversalIdentifier: HOST_OBJECT, + relationTargetObjectUniversalIdentifier: RELATION_TARGET_OBJECT, + }); + const forwardSide = getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: APP, + objectUniversalIdentifier: RELATION_TARGET_OBJECT, + relationTargetObjectUniversalIdentifier: HOST_OBJECT, + }); + + expect(reverseSide).not.toBe(forwardSide); + }); + + it('cannot collide with a name-based field identifier for any real field name', () => { + // The seed collides only if a field name equals + // `systemRelation:${relationTargetUID}`, which is unreachable because field + // names cannot contain colons. Any real (colon-free) field name on the host + // object therefore derives a distinct id. + const systemRelationIdentifier = getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: APP, + objectUniversalIdentifier: HOST_OBJECT, + relationTargetObjectUniversalIdentifier: RELATION_TARGET_OBJECT, + }); + const nameBasedIdentifier = getFieldUniversalIdentifier({ + applicationUniversalIdentifier: APP, + objectUniversalIdentifier: HOST_OBJECT, + name: 'targetCompany', + }); + + expect(systemRelationIdentifier).not.toBe(nameBasedIdentifier); + }); +}); diff --git a/packages/twenty-shared/src/application/deterministic-identifier/get-field-universal-identifier.util.ts b/packages/twenty-shared/src/application/deterministic-identifier/get-field-universal-identifier.util.ts index 7eeea8098b..e50be40329 100644 --- a/packages/twenty-shared/src/application/deterministic-identifier/get-field-universal-identifier.util.ts +++ b/packages/twenty-shared/src/application/deterministic-identifier/get-field-universal-identifier.util.ts @@ -1,6 +1,5 @@ import { computeDeterministicUuid } from '@/application/deterministic-identifier/compute-deterministic-uuid.util'; -// A field is identified by its name within its object. export const getFieldUniversalIdentifier = ({ applicationUniversalIdentifier, objectUniversalIdentifier, diff --git a/packages/twenty-shared/src/application/deterministic-identifier/get-system-relation-field-universal-identifier.util.ts b/packages/twenty-shared/src/application/deterministic-identifier/get-system-relation-field-universal-identifier.util.ts new file mode 100644 index 0000000000..f2459d6992 --- /dev/null +++ b/packages/twenty-shared/src/application/deterministic-identifier/get-system-relation-field-universal-identifier.util.ts @@ -0,0 +1,16 @@ +import { computeDeterministicUuid } from '@/application/deterministic-identifier/compute-deterministic-uuid.util'; + +export const getSystemRelationFieldUniversalIdentifier = ({ + applicationUniversalIdentifier, + objectUniversalIdentifier, + relationTargetObjectUniversalIdentifier, +}: { + applicationUniversalIdentifier: string; + objectUniversalIdentifier: string; + relationTargetObjectUniversalIdentifier: string; +}): string => + computeDeterministicUuid({ + entityNamespace: 'fieldMetadata', + value: `${objectUniversalIdentifier}:systemRelation:${relationTargetObjectUniversalIdentifier}`, + applicationUniversalIdentifier, + }); diff --git a/packages/twenty-shared/src/application/index.ts b/packages/twenty-shared/src/application/index.ts index 80c63afa8b..0e41ebad65 100644 --- a/packages/twenty-shared/src/application/index.ts +++ b/packages/twenty-shared/src/application/index.ts @@ -75,6 +75,7 @@ export { getRoleUniversalIdentifier } from './deterministic-identifier/get-role- export { getSearchFieldUniversalIdentifier } from './deterministic-identifier/get-search-field-universal-identifier.util'; 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 { getViewFieldGroupUniversalIdentifier } from './deterministic-identifier/get-view-field-group-universal-identifier.util'; export { getViewFieldUniversalIdentifier } from './deterministic-identifier/get-view-field-universal-identifier.util'; export { getViewFilterUniversalIdentifier } from './deterministic-identifier/get-view-filter-universal-identifier.util'; diff --git a/packages/twenty-shared/src/metadata/__tests__/__snapshots__/standardObjectUniversalIdentifiers.test.ts.snap b/packages/twenty-shared/src/metadata/__tests__/__snapshots__/standardObjectUniversalIdentifiers.test.ts.snap index d7e1bcfe39..5267b7afb0 100644 --- a/packages/twenty-shared/src/metadata/__tests__/__snapshots__/standardObjectUniversalIdentifiers.test.ts.snap +++ b/packages/twenty-shared/src/metadata/__tests__/__snapshots__/standardObjectUniversalIdentifiers.test.ts.snap @@ -35,25 +35,25 @@ exports[`STANDARD_OBJECTS universal identifiers should never change without an e "universalIdentifier": "e3222f8c-aad5-5873-9a10-648908f48f81", }, "targetCompany": { - "universalIdentifier": "20202020-ceab-4a28-b546-73b06b4c08d5", + "universalIdentifier": "a69093bb-6f2a-5ad6-90b1-0d78e9528019", }, "targetDashboard": { - "universalIdentifier": "20202020-5324-43f3-9dbf-1a33e7de0ce6", + "universalIdentifier": "3af87717-6757-5d61-940e-3016cff35cdc", }, "targetNote": { - "universalIdentifier": "20202020-4f4b-4503-a6fc-6b982f3dffb5", + "universalIdentifier": "a627557c-db37-53be-8a59-9aa2d45cd7ee", }, "targetOpportunity": { - "universalIdentifier": "20202020-7374-499d-bea3-9354890755b5", + "universalIdentifier": "523ff02a-1af7-5799-8730-01e2beea7150", }, "targetPerson": { - "universalIdentifier": "20202020-0158-4aa2-965c-5cdafe21ffa2", + "universalIdentifier": "721ddb1f-468d-535a-9809-cb3429a52e06", }, "targetTask": { - "universalIdentifier": "20202020-51e5-4621-9cf8-215487951c4b", + "universalIdentifier": "ace06117-9980-5506-adae-74c71556728b", }, "targetWorkflow": { - "universalIdentifier": "20202020-f1e8-4c9d-8a7b-3f5e1d2c9a8b", + "universalIdentifier": "edbe3ea7-b9ff-5b23-9a81-ef53a190c873", }, "updatedAt": { "universalIdentifier": "8ac2b742-975b-5633-9f7f-fcf9920a72e8", @@ -734,7 +734,7 @@ exports[`STANDARD_OBJECTS universal identifiers should never change without an e "universalIdentifier": "60f533b7-2166-4071-a767-ceb0286822fd", }, "attachments": { - "universalIdentifier": "20202020-c1b5-4120-b0f0-987ca401ed53", + "universalIdentifier": "a4399ae3-fe89-59fe-a282-aec77d4e63a8", }, "createdAt": { "universalIdentifier": "d206d586-fd61-56e1-80a8-9993beb7be0b", @@ -758,7 +758,7 @@ exports[`STANDARD_OBJECTS universal identifiers should never change without an e "universalIdentifier": "20202020-4d99-4e2e-a84c-4a27837b1ece", }, "noteTargets": { - "universalIdentifier": "20202020-bae0-4556-a74a-a9c686f77a88", + "universalIdentifier": "0b28db77-8099-5ce3-a11f-86a5778d7740", }, "opportunities": { "universalIdentifier": "20202020-add3-4658-8e23-d70dccb6d0ec", @@ -773,10 +773,10 @@ exports[`STANDARD_OBJECTS universal identifiers should never change without an e "universalIdentifier": "d043190d-3335-5463-b98d-eecbf7b41c65", }, "taskTargets": { - "universalIdentifier": "20202020-cb17-4a61-8f8f-3be6730480de", + "universalIdentifier": "9d7b0087-7037-55dc-bb59-456064455694", }, "timelineActivities": { - "universalIdentifier": "20202020-0414-4daf-9c0d-64fe7b27f89f", + "universalIdentifier": "28ec0f8f-ad23-5317-b2d9-1a487bcefee2", }, "updatedAt": { "universalIdentifier": "a1add354-649b-5ab6-9fc4-a5ab112110ac", @@ -893,7 +893,7 @@ exports[`STANDARD_OBJECTS universal identifiers should never change without an e "dashboard": { "fields": { "attachments": { - "universalIdentifier": "20202020-bf6f-4220-8c55-2764f1175870", + "universalIdentifier": "6e0748cd-8876-53f9-b78d-8bdb3c71883e", }, "createdAt": { "universalIdentifier": "78bc7f2f-9060-571d-a690-de20ac22622b", @@ -917,7 +917,7 @@ exports[`STANDARD_OBJECTS universal identifiers should never change without an e "universalIdentifier": "32804a7c-2b8c-57cd-8a41-963d5f4b1503", }, "timelineActivities": { - "universalIdentifier": "99c330c0-5b7d-4276-a764-aed84499dfb5", + "universalIdentifier": "82711ab6-00a2-57e7-9db8-9f77dde9944c", }, "title": { "universalIdentifier": "20202020-20ee-4091-95dc-44b57eda3a89", @@ -1107,7 +1107,7 @@ exports[`STANDARD_OBJECTS universal identifiers should never change without an e "universalIdentifier": "7251544c-b07a-4f0d-9d0a-48514367f230", }, "timelineActivities": { - "universalIdentifier": "d4e5f607-1829-4da3-8eb4-25f607182930", + "universalIdentifier": "b4dd1932-bcfa-50c6-ada7-37de5d9c2825", }, "unsubscribeTopicId": { "universalIdentifier": "0648e7ad-1769-4ff6-a4d5-72da79ef169c", @@ -1403,7 +1403,7 @@ exports[`STANDARD_OBJECTS universal identifiers should never change without an e "universalIdentifier": "a163f11e-cf13-5a7e-8ac2-ff522d9a7a79", }, "timelineActivities": { - "universalIdentifier": "e0a5b2c3-4d6f-4e81-9a02-1b3c4d5e6f70", + "universalIdentifier": "e0fbfc29-9b77-52c0-935e-a21af8e10325", }, "updatedAt": { "universalIdentifier": "fbdc216f-2f82-59c7-af5c-daa6e6009e46", @@ -1668,7 +1668,7 @@ exports[`STANDARD_OBJECTS universal identifiers should never change without an e "note": { "fields": { "attachments": { - "universalIdentifier": "20202020-4986-4c92-bf19-39934b149b16", + "universalIdentifier": "b5e03b44-f726-5eae-9ea9-141d074f2120", }, "bodyV2": { "universalIdentifier": "20202020-a7bb-4d94-be51-8f25181502c8", @@ -1695,7 +1695,7 @@ exports[`STANDARD_OBJECTS universal identifiers should never change without an e "universalIdentifier": "0ad23e30-4fe2-5200-88e5-dd14a9e5438a", }, "timelineActivities": { - "universalIdentifier": "20202020-7030-42f8-929c-1a57b25d6bce", + "universalIdentifier": "dd80c0fb-074a-5ca2-8596-dbb967f20155", }, "title": { "universalIdentifier": "20202020-faeb-4c76-8ba6-ccbb0b4a965f", @@ -1797,13 +1797,13 @@ exports[`STANDARD_OBJECTS universal identifiers should never change without an e "universalIdentifier": "d853a31b-f266-57a1-9a9f-ecc187864a01", }, "targetCompany": { - "universalIdentifier": "c500fbc0-d6f2-4982-a959-5a755431696c", + "universalIdentifier": "26b5a2d6-3296-5660-8f5b-a5710c11ae33", }, "targetOpportunity": { - "universalIdentifier": "20202020-4e42-417a-a705-76581c9ade79", + "universalIdentifier": "27519e5b-b16a-50cc-8de1-c5d6a2ff5c61", }, "targetPerson": { - "universalIdentifier": "20202020-38ca-4aab-92f5-8a605ca2e4c5", + "universalIdentifier": "b445c148-eacf-56a8-bc21-e67e7ae93302", }, "updatedAt": { "universalIdentifier": "18a0222a-d8d3-5a5a-89a7-55f0873b0d20", @@ -1861,7 +1861,7 @@ exports[`STANDARD_OBJECTS universal identifiers should never change without an e "universalIdentifier": "20202020-583e-4642-8533-db761d5fa82f", }, "attachments": { - "universalIdentifier": "20202020-87c7-4118-83d6-2f4031005209", + "universalIdentifier": "37d15d4b-7441-5bbc-8a5f-228f2d62abf4", }, "closeDate": { "universalIdentifier": "20202020-527e-44d6-b1ac-c4158d307b97", @@ -1885,7 +1885,7 @@ exports[`STANDARD_OBJECTS universal identifiers should never change without an e "universalIdentifier": "20202020-8609-4f65-a2d9-44009eb422b5", }, "noteTargets": { - "universalIdentifier": "20202020-dd3f-42d5-a382-db58aabf43d3", + "universalIdentifier": "ee980fc9-5f51-5986-9fbe-ccf5a103f595", }, "owner": { "universalIdentifier": "20202020-be7e-4d1e-8e19-3d5c7c4b9f2a", @@ -1903,10 +1903,10 @@ exports[`STANDARD_OBJECTS universal identifiers should never change without an e "universalIdentifier": "20202020-6f76-477d-8551-28cd65b2b4b9", }, "taskTargets": { - "universalIdentifier": "20202020-59c0-4179-a208-4a255f04a5be", + "universalIdentifier": "3e20f0e5-c0f5-5af4-8cac-02a65ecc4e86", }, "timelineActivities": { - "universalIdentifier": "20202020-30e2-421f-96c7-19c69d1cf631", + "universalIdentifier": "64f26c76-9427-55a1-82e6-332b495878ad", }, "updatedAt": { "universalIdentifier": "e0270cd8-986d-5dab-aaa1-1fe813969a6a", @@ -2057,7 +2057,7 @@ exports[`STANDARD_OBJECTS universal identifiers should never change without an e "person": { "fields": { "attachments": { - "universalIdentifier": "20202020-cd97-451f-87fa-bcb789bdbf3a", + "universalIdentifier": "54dd92e1-67bf-5381-86f3-c747976dcdaf", }, "avatarFile": { "universalIdentifier": "20202020-a7c9-4e3d-8f1b-2d5a6b7c8e9f", @@ -2102,7 +2102,7 @@ exports[`STANDARD_OBJECTS universal identifiers should never change without an e "universalIdentifier": "20202020-3875-44d5-8c33-a6239011cab8", }, "noteTargets": { - "universalIdentifier": "20202020-c8fc-4258-8250-15905d3fcfec", + "universalIdentifier": "1c0e0dfe-a01f-58d6-8948-08b93164d03c", }, "phones": { "universalIdentifier": "20202020-0638-448e-8825-439134618022", @@ -2117,10 +2117,10 @@ exports[`STANDARD_OBJECTS universal identifiers should never change without an e "universalIdentifier": "efe6cbc6-1c6e-5a6e-91c9-17142d3bd947", }, "taskTargets": { - "universalIdentifier": "20202020-584b-4d3e-88b6-53ab1fa03c3a", + "universalIdentifier": "ddea86e6-5766-5798-ac32-14fbab6089f6", }, "timelineActivities": { - "universalIdentifier": "20202020-a43e-4873-9c23-e522de906ce5", + "universalIdentifier": "32d7483f-6227-528b-bc65-62d9cf858cd8", }, "updatedAt": { "universalIdentifier": "962cedc2-cb8f-5f33-b5e1-256aabe6f14e", @@ -2252,7 +2252,7 @@ exports[`STANDARD_OBJECTS universal identifiers should never change without an e "universalIdentifier": "20202020-065a-4f42-a906-e20422c1753f", }, "attachments": { - "universalIdentifier": "20202020-794d-4783-a8ff-cecdb15be139", + "universalIdentifier": "d88469f5-b558-5e11-ab85-66f9ffcaecc0", }, "bodyV2": { "universalIdentifier": "20202020-4aa0-4ae8-898d-7df0afd47ab1", @@ -2285,7 +2285,7 @@ exports[`STANDARD_OBJECTS universal identifiers should never change without an e "universalIdentifier": "20202020-de9c-4d0e-a452-713d4a3e5fc7", }, "timelineActivities": { - "universalIdentifier": "20202020-c778-4278-99ee-23a2837aee64", + "universalIdentifier": "9058db0a-b156-5c9e-999f-c89387ef4a9b", }, "title": { "universalIdentifier": "20202020-b386-4cb7-aa5a-08d4a4d92680", @@ -2481,13 +2481,13 @@ exports[`STANDARD_OBJECTS universal identifiers should never change without an e "universalIdentifier": "821c713e-bf1c-569d-9f0c-8e10e80eee5e", }, "targetCompany": { - "universalIdentifier": "20202020-4703-4a4e-948c-487b0c60a92c", + "universalIdentifier": "805997f5-d539-5676-9aa8-0de602ac4152", }, "targetOpportunity": { - "universalIdentifier": "20202020-6cb2-4c01-a9a5-aca3dbc11d41", + "universalIdentifier": "ca354769-b50e-531b-b745-12ca12ad44ee", }, "targetPerson": { - "universalIdentifier": "20202020-c8a0-4e85-a016-87e2349cfbec", + "universalIdentifier": "c0a02b7a-a356-537a-96c8-66d1f8994100", }, "task": { "universalIdentifier": "20202020-e881-457a-8758-74aaef4ae78a", @@ -2581,37 +2581,37 @@ exports[`STANDARD_OBJECTS universal identifiers should never change without an e "universalIdentifier": "23a0482b-f41b-5e13-9b3e-356c5aaa90a3", }, "targetCompany": { - "universalIdentifier": "20202020-04ad-4221-a744-7a8278a5ce21", + "universalIdentifier": "500e6797-19a2-5f11-b239-259b58765533", }, "targetDashboard": { - "universalIdentifier": "20202020-7864-48f5-af7c-9e4b60140948", + "universalIdentifier": "0a1c0f53-5cbe-5ae8-8dde-e0dd7a7d136f", }, "targetMessageCampaign": { - "universalIdentifier": "b2c3d4e5-6f70-4b81-8c92-03d4e5f60718", + "universalIdentifier": "22194f8d-a9f3-5f13-ae0f-eb23c6338cea", }, "targetMessageList": { - "universalIdentifier": "d9f4a1b2-3c5e-4d70-8e91-0a2b3c4d5e6f", + "universalIdentifier": "e4367803-1b53-537c-b380-aa7c31a2a2cf", }, "targetNote": { - "universalIdentifier": "20202020-ec55-4135-8da5-3a20badc0156", + "universalIdentifier": "c2202780-3676-511d-810f-725b0f3a4fdb", }, "targetOpportunity": { - "universalIdentifier": "20202020-7664-4a35-a3df-580d389fd527", + "universalIdentifier": "1598c13c-d539-52c8-a56c-974a6b724393", }, "targetPerson": { - "universalIdentifier": "20202020-c414-45b9-a60a-ac27aa96229f", + "universalIdentifier": "e2477375-5aeb-53bd-8ee1-97e804a73f9d", }, "targetTask": { - "universalIdentifier": "20202020-b2f5-415c-9135-a31dfe49501b", + "universalIdentifier": "9022391d-5b5b-519e-be9f-fa954856bb29", }, "targetWorkflow": { - "universalIdentifier": "20202020-616c-4ad3-a2e9-c477c341e295", + "universalIdentifier": "7bdf04f6-a598-57b2-b485-5263c25e1257", }, "targetWorkflowRun": { - "universalIdentifier": "20202020-96f0-401b-9186-a3a0759225ac", + "universalIdentifier": "b1df837a-e304-5cde-97e6-ab8348739774", }, "targetWorkflowVersion": { - "universalIdentifier": "20202020-74f1-4711-a129-e14ca0ecd744", + "universalIdentifier": "2da44ef9-dc63-5c9c-a9fb-58933ed03bc2", }, "updatedAt": { "universalIdentifier": "01b33b31-f011-5b55-942f-5bf3ec56f9b3", @@ -2714,7 +2714,7 @@ exports[`STANDARD_OBJECTS universal identifiers should never change without an e "workflow": { "fields": { "attachments": { - "universalIdentifier": "20202020-4a8c-4e2d-9b1c-7e5f3a2b4c6d", + "universalIdentifier": "78d27c16-be35-54d2-988a-451dc24f77ff", }, "automatedTriggers": { "universalIdentifier": "20202020-3319-4234-a34c-117ecad2b8a9", @@ -2753,7 +2753,7 @@ exports[`STANDARD_OBJECTS universal identifiers should never change without an e "universalIdentifier": "20202020-357c-4432-8c50-8c31b4a552d9", }, "timelineActivities": { - "universalIdentifier": "20202020-906e-486a-a798-131a5f081faf", + "universalIdentifier": "0ca041cd-598d-5c6b-881c-fe89ee97a0bc", }, "updatedAt": { "universalIdentifier": "b277195f-91aa-5e90-9362-3b2a162aaa63", @@ -2923,7 +2923,7 @@ exports[`STANDARD_OBJECTS universal identifiers should never change without an e "universalIdentifier": "20202020-7c4e-4e1a-8fc1-1e3a55d6c2a1", }, "timelineActivities": { - "universalIdentifier": "20202020-af4d-4eb0-babc-eb960a45b356", + "universalIdentifier": "1e8e4e87-48c3-54d5-bd75-5cda9cf393b0", }, "updatedAt": { "universalIdentifier": "363b5eb8-c334-5c0f-9568-7e1320d4c76c", @@ -3061,7 +3061,7 @@ exports[`STANDARD_OBJECTS universal identifiers should never change without an e "universalIdentifier": "20202020-5988-4a64-b94a-1f9b7b989039", }, "timelineActivities": { - "universalIdentifier": "20202020-fcb0-4695-b17e-3b43a421c633", + "universalIdentifier": "4be90325-f6ca-5931-a977-1194bb4b3d5c", }, "trigger": { "universalIdentifier": "20202020-4eae-43e7-86e0-212b41a30b48", diff --git a/packages/twenty-shared/src/metadata/constants/standard-object-universal-identifiers.constant.ts b/packages/twenty-shared/src/metadata/constants/standard-object-universal-identifiers.constant.ts new file mode 100644 index 0000000000..edee295d6a --- /dev/null +++ b/packages/twenty-shared/src/metadata/constants/standard-object-universal-identifiers.constant.ts @@ -0,0 +1,25 @@ +// Object universal identifiers referenced from another object's declaration +// (default relation builders need both the host and the source object +// identifiers, and an object literal cannot reference its sibling keys). +// Each STANDARD_OBJECTS entry involved in the default relations reads its own +// universalIdentifier from this record so there is a single source of truth. +export const STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS = { + timelineActivity: '20202020-6736-4337-b5c4-8b39fae325a5', + attachment: '20202020-bd3d-4c60-8dca-571c71d4447a', + noteTarget: '20202020-fff0-4b44-be82-bda313884400', + taskTarget: '20202020-5a9a-44e8-95df-771cd06d0fb1', + person: '20202020-e674-48e5-a542-72570eee7213', + company: '20202020-b374-4779-a561-80086cb2e17f', + opportunity: '20202020-9549-49dd-b2b2-883999db8938', + note: '20202020-0b00-45cd-b6f6-6cd806fc6804', + task: '20202020-1ba1-48ba-bc83-ef7e5990ed10', + workflow: '20202020-62be-406c-b9ca-8caa50d51392', + workflowVersion: '20202020-d65d-4ab9-9344-d77bfb376a3d', + workflowRun: '20202020-4e28-4e95-a9d7-6c00874f843c', + dashboard: '20202020-3840-4b6d-9425-0c5188b05ca8', + messageList: '826561ea-4816-411c-baa0-eec5e6ca8866', + messageCampaign: '238acb94-dd4c-4036-bc55-19b99d821efd', +} as const; + +export type StandardObjectWithUniversalIdentifierName = + keyof typeof STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS; 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 e49d791d3e..4f2f0d752c 100644 --- a/packages/twenty-shared/src/metadata/constants/standard-object.constant.ts +++ b/packages/twenty-shared/src/metadata/constants/standard-object.constant.ts @@ -1,3 +1,6 @@ +import { TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER } from '@/application/constants/TwentyStandardApplicationUniversalIdentifier'; +import { getSystemRelationFieldUniversalIdentifier } from '@/application/deterministic-identifier/get-system-relation-field-universal-identifier.util'; +import { STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS } from '@/metadata/constants/standard-object-universal-identifiers.constant'; import { buildStandardObjectSystemFields } from '@/metadata/utils/internal/build-standard-object-system-fields.util'; // Important notice: @@ -11,10 +14,10 @@ import { buildStandardObjectSystemFields } from '@/metadata/utils/internal/build // hardcoded universal identifier. export const STANDARD_OBJECTS = { attachment: { - universalIdentifier: '20202020-bd3d-4c60-8dca-571c71d4447a', + universalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.attachment, fields: { ...buildStandardObjectSystemFields( - '20202020-bd3d-4c60-8dca-571c71d4447a', + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.attachment, ), name: { universalIdentifier: '20202020-87a5-48f8-bbf7-ade388825a57' }, file: { universalIdentifier: '20202020-15db-460e-8166-c7b5d87ad4be' }, @@ -25,25 +28,74 @@ export const STANDARD_OBJECTS = { universalIdentifier: '20202020-8c3f-4d9e-9a1b-2e5f7a8c9d0e', }, targetTask: { - universalIdentifier: '20202020-51e5-4621-9cf8-215487951c4b', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.attachment, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.task, + }), }, targetNote: { - universalIdentifier: '20202020-4f4b-4503-a6fc-6b982f3dffb5', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.attachment, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.note, + }), }, targetPerson: { - universalIdentifier: '20202020-0158-4aa2-965c-5cdafe21ffa2', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.attachment, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person, + }), }, targetCompany: { - universalIdentifier: '20202020-ceab-4a28-b546-73b06b4c08d5', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.attachment, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company, + }), }, targetOpportunity: { - universalIdentifier: '20202020-7374-499d-bea3-9354890755b5', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.attachment, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity, + }), }, targetDashboard: { - universalIdentifier: '20202020-5324-43f3-9dbf-1a33e7de0ce6', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.attachment, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.dashboard, + }), }, targetWorkflow: { - universalIdentifier: '20202020-f1e8-4c9d-8a7b-3f5e1d2c9a8b', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.attachment, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.workflow, + }), }, }, morphIds: { @@ -593,10 +645,10 @@ export const STANDARD_OBJECTS = { }, }, company: { - universalIdentifier: '20202020-b374-4779-a561-80086cb2e17f', + universalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company, fields: { ...buildStandardObjectSystemFields( - '20202020-b374-4779-a561-80086cb2e17f', + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company, ), name: { universalIdentifier: '20202020-4d99-4e2e-a84c-4a27837b1ece' }, domainName: { @@ -614,19 +666,47 @@ export const STANDARD_OBJECTS = { universalIdentifier: '20202020-95b8-4e10-9881-edb5d4765f9d', }, taskTargets: { - universalIdentifier: '20202020-cb17-4a61-8f8f-3be6730480de', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.taskTarget, + }), }, noteTargets: { - universalIdentifier: '20202020-bae0-4556-a74a-a9c686f77a88', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.noteTarget, + }), }, opportunities: { universalIdentifier: '20202020-add3-4658-8e23-d70dccb6d0ec', }, attachments: { - universalIdentifier: '20202020-c1b5-4120-b0f0-987ca401ed53', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.attachment, + }), }, timelineActivities: { - universalIdentifier: '20202020-0414-4daf-9c0d-64fe7b27f89f', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.timelineActivity, + }), }, }, indexes: { @@ -734,20 +814,34 @@ export const STANDARD_OBJECTS = { }, }, dashboard: { - universalIdentifier: '20202020-3840-4b6d-9425-0c5188b05ca8', + universalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.dashboard, fields: { ...buildStandardObjectSystemFields( - '20202020-3840-4b6d-9425-0c5188b05ca8', + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.dashboard, ), title: { universalIdentifier: '20202020-20ee-4091-95dc-44b57eda3a89' }, pageLayoutId: { universalIdentifier: '20202020-bb53-4648-aa36-1d9d54e6f7f2', }, timelineActivities: { - universalIdentifier: '99c330c0-5b7d-4276-a764-aed84499dfb5', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.dashboard, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.timelineActivity, + }), }, attachments: { - universalIdentifier: '20202020-bf6f-4220-8c55-2764f1175870', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.dashboard, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.attachment, + }), }, }, indexes: { @@ -776,10 +870,10 @@ export const STANDARD_OBJECTS = { }, }, messageCampaign: { - universalIdentifier: '238acb94-dd4c-4036-bc55-19b99d821efd', + universalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.messageCampaign, fields: { ...buildStandardObjectSystemFields( - '238acb94-dd4c-4036-bc55-19b99d821efd', + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.messageCampaign, ), subject: { universalIdentifier: '7251544c-b07a-4f0d-9d0a-48514367f230' }, bodyTemplate: { @@ -807,7 +901,14 @@ export const STANDARD_OBJECTS = { }, list: { universalIdentifier: 'cb24dcdf-f0e8-4c71-8cff-70b714e86530' }, timelineActivities: { - universalIdentifier: 'd4e5f607-1829-4da3-8eb4-25f607182930', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.messageCampaign, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.timelineActivity, + }), }, messages: { universalIdentifier: 'e5a177a7-512b-4778-928e-69777a528f7c' }, recipients: { @@ -867,10 +968,10 @@ export const STANDARD_OBJECTS = { }, }, messageList: { - universalIdentifier: '826561ea-4816-411c-baa0-eec5e6ca8866', + universalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.messageList, fields: { ...buildStandardObjectSystemFields( - '826561ea-4816-411c-baa0-eec5e6ca8866', + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.messageList, ), name: { universalIdentifier: '69b9ed8b-7b26-4108-894f-05700ef7e8ee' }, members: { @@ -880,7 +981,14 @@ export const STANDARD_OBJECTS = { universalIdentifier: 'e098d838-31ab-4812-91a8-f055f45a6832', }, timelineActivities: { - universalIdentifier: 'e0a5b2c3-4d6f-4e81-9a02-1b3c4d5e6f70', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.messageList, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.timelineActivity, + }), }, }, indexes: { @@ -1300,10 +1408,10 @@ export const STANDARD_OBJECTS = { }, }, note: { - universalIdentifier: '20202020-0b00-45cd-b6f6-6cd806fc6804', + universalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.note, fields: { ...buildStandardObjectSystemFields( - '20202020-0b00-45cd-b6f6-6cd806fc6804', + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.note, ), title: { universalIdentifier: '20202020-faeb-4c76-8ba6-ccbb0b4a965f' }, bodyV2: { universalIdentifier: '20202020-a7bb-4d94-be51-8f25181502c8' }, @@ -1311,10 +1419,22 @@ export const STANDARD_OBJECTS = { universalIdentifier: '20202020-1f25-43fe-8b00-af212fdde823', }, attachments: { - universalIdentifier: '20202020-4986-4c92-bf19-39934b149b16', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.note, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.attachment, + }), }, timelineActivities: { - universalIdentifier: '20202020-7030-42f8-929c-1a57b25d6bce', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.note, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.timelineActivity, + }), }, }, indexes: { @@ -1383,20 +1503,41 @@ export const STANDARD_OBJECTS = { }, }, noteTarget: { - universalIdentifier: '20202020-fff0-4b44-be82-bda313884400', + universalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.noteTarget, fields: { ...buildStandardObjectSystemFields( - '20202020-fff0-4b44-be82-bda313884400', + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.noteTarget, ), note: { universalIdentifier: '20202020-57f3-4f50-9599-fc0f671df003' }, targetPerson: { - universalIdentifier: '20202020-38ca-4aab-92f5-8a605ca2e4c5', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.noteTarget, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person, + }), }, targetCompany: { - universalIdentifier: 'c500fbc0-d6f2-4982-a959-5a755431696c', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.noteTarget, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company, + }), }, targetOpportunity: { - universalIdentifier: '20202020-4e42-417a-a705-76581c9ade79', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.noteTarget, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity, + }), }, }, morphIds: { @@ -1440,10 +1581,10 @@ export const STANDARD_OBJECTS = { }, }, opportunity: { - universalIdentifier: '20202020-9549-49dd-b2b2-883999db8938', + universalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity, fields: { ...buildStandardObjectSystemFields( - '20202020-9549-49dd-b2b2-883999db8938', + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity, ), name: { universalIdentifier: '20202020-8609-4f65-a2d9-44009eb422b5' }, amount: { universalIdentifier: '20202020-583e-4642-8533-db761d5fa82f' }, @@ -1457,16 +1598,44 @@ export const STANDARD_OBJECTS = { company: { universalIdentifier: '20202020-cbac-457e-b565-adece5fc815f' }, owner: { universalIdentifier: '20202020-be7e-4d1e-8e19-3d5c7c4b9f2a' }, taskTargets: { - universalIdentifier: '20202020-59c0-4179-a208-4a255f04a5be', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.taskTarget, + }), }, noteTargets: { - universalIdentifier: '20202020-dd3f-42d5-a382-db58aabf43d3', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.noteTarget, + }), }, attachments: { - universalIdentifier: '20202020-87c7-4118-83d6-2f4031005209', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.attachment, + }), }, timelineActivities: { - universalIdentifier: '20202020-30e2-421f-96c7-19c69d1cf631', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.timelineActivity, + }), }, }, indexes: { @@ -1608,10 +1777,10 @@ export const STANDARD_OBJECTS = { }, }, person: { - universalIdentifier: '20202020-e674-48e5-a542-72570eee7213', + universalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person, fields: { ...buildStandardObjectSystemFields( - '20202020-e674-48e5-a542-72570eee7213', + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person, ), name: { universalIdentifier: '20202020-3875-44d5-8c33-a6239011cab8' }, emails: { universalIdentifier: '20202020-3c51-43fa-8b6e-af39e29368ab' }, @@ -1631,13 +1800,34 @@ export const STANDARD_OBJECTS = { universalIdentifier: '20202020-911b-4a7d-b67b-918aa9a5b33a', }, taskTargets: { - universalIdentifier: '20202020-584b-4d3e-88b6-53ab1fa03c3a', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.taskTarget, + }), }, noteTargets: { - universalIdentifier: '20202020-c8fc-4258-8250-15905d3fcfec', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.noteTarget, + }), }, attachments: { - universalIdentifier: '20202020-cd97-451f-87fa-bcb789bdbf3a', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.attachment, + }), }, messageParticipants: { universalIdentifier: '20202020-498e-4c61-8158-fa04f0638334', @@ -1646,7 +1836,14 @@ export const STANDARD_OBJECTS = { universalIdentifier: '20202020-52ee-45e9-a702-b64b3753e3a9', }, timelineActivities: { - universalIdentifier: '20202020-a43e-4873-9c23-e522de906ce5', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.timelineActivity, + }), }, listMemberships: { universalIdentifier: '8b8d1be0-4c94-4413-a2c9-c7ede205a81d', @@ -1769,10 +1966,10 @@ export const STANDARD_OBJECTS = { }, }, task: { - universalIdentifier: '20202020-1ba1-48ba-bc83-ef7e5990ed10', + universalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.task, fields: { ...buildStandardObjectSystemFields( - '20202020-1ba1-48ba-bc83-ef7e5990ed10', + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.task, ), title: { universalIdentifier: '20202020-b386-4cb7-aa5a-08d4a4d92680' }, bodyV2: { universalIdentifier: '20202020-4aa0-4ae8-898d-7df0afd47ab1' }, @@ -1782,11 +1979,23 @@ export const STANDARD_OBJECTS = { universalIdentifier: '20202020-de9c-4d0e-a452-713d4a3e5fc7', }, attachments: { - universalIdentifier: '20202020-794d-4783-a8ff-cecdb15be139', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.task, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.attachment, + }), }, assignee: { universalIdentifier: '20202020-065a-4f42-a906-e20422c1753f' }, timelineActivities: { - universalIdentifier: '20202020-c778-4278-99ee-23a2837aee64', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.task, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.timelineActivity, + }), }, }, indexes: { @@ -1952,20 +2161,41 @@ export const STANDARD_OBJECTS = { }, }, taskTarget: { - universalIdentifier: '20202020-5a9a-44e8-95df-771cd06d0fb1', + universalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.taskTarget, fields: { ...buildStandardObjectSystemFields( - '20202020-5a9a-44e8-95df-771cd06d0fb1', + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.taskTarget, ), task: { universalIdentifier: '20202020-e881-457a-8758-74aaef4ae78a' }, targetPerson: { - universalIdentifier: '20202020-c8a0-4e85-a016-87e2349cfbec', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.taskTarget, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person, + }), }, targetCompany: { - universalIdentifier: '20202020-4703-4a4e-948c-487b0c60a92c', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.taskTarget, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company, + }), }, targetOpportunity: { - universalIdentifier: '20202020-6cb2-4c01-a9a5-aca3dbc11d41', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.taskTarget, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity, + }), }, }, morphIds: { @@ -2009,10 +2239,10 @@ export const STANDARD_OBJECTS = { }, }, timelineActivity: { - universalIdentifier: '20202020-6736-4337-b5c4-8b39fae325a5', + universalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.timelineActivity, fields: { ...buildStandardObjectSystemFields( - '20202020-6736-4337-b5c4-8b39fae325a5', + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.timelineActivity, ), name: { universalIdentifier: '20202020-7207-46e8-9dab-849505ae8497' }, happensAt: { @@ -2025,37 +2255,114 @@ export const STANDARD_OBJECTS = { universalIdentifier: '20202020-af23-4479-9a30-868edc474b36', }, targetPerson: { - universalIdentifier: '20202020-c414-45b9-a60a-ac27aa96229f', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.timelineActivity, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person, + }), }, targetCompany: { - universalIdentifier: '20202020-04ad-4221-a744-7a8278a5ce21', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.timelineActivity, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company, + }), }, targetOpportunity: { - universalIdentifier: '20202020-7664-4a35-a3df-580d389fd527', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.timelineActivity, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity, + }), }, targetTask: { - universalIdentifier: '20202020-b2f5-415c-9135-a31dfe49501b', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.timelineActivity, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.task, + }), }, targetNote: { - universalIdentifier: '20202020-ec55-4135-8da5-3a20badc0156', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.timelineActivity, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.note, + }), }, targetWorkflow: { - universalIdentifier: '20202020-616c-4ad3-a2e9-c477c341e295', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.timelineActivity, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.workflow, + }), }, targetWorkflowVersion: { - universalIdentifier: '20202020-74f1-4711-a129-e14ca0ecd744', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.timelineActivity, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.workflowVersion, + }), }, targetWorkflowRun: { - universalIdentifier: '20202020-96f0-401b-9186-a3a0759225ac', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.timelineActivity, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.workflowRun, + }), }, targetDashboard: { - universalIdentifier: '20202020-7864-48f5-af7c-9e4b60140948', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.timelineActivity, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.dashboard, + }), }, targetMessageList: { - universalIdentifier: 'd9f4a1b2-3c5e-4d70-8e91-0a2b3c4d5e6f', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.timelineActivity, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.messageList, + }), }, targetMessageCampaign: { - universalIdentifier: 'b2c3d4e5-6f70-4b81-8c92-03d4e5f60718', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.timelineActivity, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.messageCampaign, + }), }, linkedRecordCachedName: { universalIdentifier: '20202020-cfdb-4bef-bbce-a29f41230934', @@ -2153,10 +2460,10 @@ export const STANDARD_OBJECTS = { }, }, workflow: { - universalIdentifier: '20202020-62be-406c-b9ca-8caa50d51392', + universalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.workflow, fields: { ...buildStandardObjectSystemFields( - '20202020-62be-406c-b9ca-8caa50d51392', + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.workflow, ), name: { universalIdentifier: '20202020-b3d3-478f-acc0-5d901e725b20' }, lastPublishedVersionId: { @@ -2172,10 +2479,24 @@ export const STANDARD_OBJECTS = { universalIdentifier: '20202020-3319-4234-a34c-117ecad2b8a9', }, timelineActivities: { - universalIdentifier: '20202020-906e-486a-a798-131a5f081faf', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.workflow, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.timelineActivity, + }), }, attachments: { - universalIdentifier: '20202020-4a8c-4e2d-9b1c-7e5f3a2b4c6d', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.workflow, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.attachment, + }), }, }, indexes: { @@ -2273,10 +2594,10 @@ export const STANDARD_OBJECTS = { }, }, workflowRun: { - universalIdentifier: '20202020-4e28-4e95-a9d7-6c00874f843c', + universalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.workflowRun, fields: { ...buildStandardObjectSystemFields( - '20202020-4e28-4e95-a9d7-6c00874f843c', + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.workflowRun, ), name: { universalIdentifier: '20202020-b840-4253-aef9-4e5013694587' }, workflowVersion: { @@ -2298,7 +2619,14 @@ export const STANDARD_OBJECTS = { universalIdentifier: '20202020-7c4e-4e1a-8fc1-1e3a55d6c2a1', }, timelineActivities: { - universalIdentifier: '20202020-af4d-4eb0-babc-eb960a45b356', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.workflowRun, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.timelineActivity, + }), }, }, indexes: { @@ -2388,10 +2716,10 @@ export const STANDARD_OBJECTS = { }, }, workflowVersion: { - universalIdentifier: '20202020-d65d-4ab9-9344-d77bfb376a3d', + universalIdentifier: STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.workflowVersion, fields: { ...buildStandardObjectSystemFields( - '20202020-d65d-4ab9-9344-d77bfb376a3d', + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.workflowVersion, ), name: { universalIdentifier: '20202020-a12f-4cca-9937-a2e40cc65509' }, workflow: { @@ -2409,7 +2737,14 @@ export const STANDARD_OBJECTS = { universalIdentifier: '20202020-58b4-46e8-b6d2-f1f3c74cf7f4', }, timelineActivities: { - universalIdentifier: '20202020-fcb0-4695-b17e-3b43a421c633', + universalIdentifier: getSystemRelationFieldUniversalIdentifier({ + applicationUniversalIdentifier: + TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER, + objectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.workflowVersion, + relationTargetObjectUniversalIdentifier: + STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.timelineActivity, + }), }, }, indexes: { diff --git a/packages/twenty-shared/src/metadata/index.ts b/packages/twenty-shared/src/metadata/index.ts index 22430415fd..ee1cc4bc58 100644 --- a/packages/twenty-shared/src/metadata/index.ts +++ b/packages/twenty-shared/src/metadata/index.ts @@ -16,6 +16,8 @@ export { ALL_METADATA_NAME } from './constants/all-metadata-name.constant'; export { DEFAULT_RELATIONS_OBJECTS_STANDARD_IDS } from './constants/default-relations-object-standard-ids.constant'; export { IDENTIFIER_MAX_CHAR_LENGTH } from './constants/identifier-max-char-length.constant'; export { RESERVED_METADATA_NAME_KEYWORDS } from './constants/reserved-metadata-name-keywords.constant'; +export type { StandardObjectWithUniversalIdentifierName } from './constants/standard-object-universal-identifiers.constant'; +export { STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS } from './constants/standard-object-universal-identifiers.constant'; export { STANDARD_OBJECTS } from './constants/standard-object.constant'; export { STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS } from './constants/standard-page-layout-universal-identifiers.constant'; export type { AllMetadataName } from './types/all-metadata-name.type';