From 5544b5dcfe7a3309f65bb74cf6aeeb992f27a00c Mon Sep 17 00:00:00 2001 From: Paul Rastoin <45004772+prastoin@users.noreply.github.com> Date: Tue, 17 Feb 2026 11:13:54 +0100 Subject: [PATCH] Fix and refactor all metadata relation (#17978) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Introduction The initial motivation was that in the workspace migration create action some universal foreign key aggregators weren't correctly deleted before returned due to constant missconfiguration image It also meant that under the hood some optimistic behavior wasn't correctly rendered for some aggregators ## Solution Refactored the `ALL_METADATA_RELATIONS` as follows: This way we can infer the FK and transpile it to a universalFK, also the aggregators are one to one instead of one versus all available Making the only manual configuration to be defined the `foreignKey` and `inverseOneToManyProperty` ``` ┌──────────────────────────────────────┐ ┌─────────────────────────────────────────────┐ │ ALL_MANY_TO_ONE_METADATA_FOREIGN_KEY│ │ ALL_ONE_TO_MANY_METADATA_RELATIONS │ │──────────────────────────────────────│ │─────────────────────────────────────────────│ │ Derived from: Entity types │ │ Derived from: Entity types │ │ │ │ │ │ Provides: │ │ Provides: │ │ • foreignKey │ │ • metadataName │ │ │ │ • flatEntityForeignKeyAggregator │ │ Standalone low-level primitive │ │ • universalFlatEntityForeignKeyAggregator │ └──────────────┬───────────────────────┘ └──────────────┬──────────────────────────────┘ │ │ │ foreignKey type + │ inverseOneToManyProperty │ universalForeignKey derivation │ keys (type constraint) │ │ ▼ ▼ ┌───────────────────────────────────────────────────────────────┐ │ ALL_MANY_TO_ONE_METADATA_RELATIONS │ │───────────────────────────────────────────────────────────────│ │ Derived from: │ │ • Entity types (metadataName, isNullable) │ │ • ALL_MANY_TO_ONE_METADATA_FOREIGN_KEY (FK → universalFK) │ │ • ALL_ONE_TO_MANY_METADATA_RELATIONS (inverse keys) │ │ │ │ Provides: │ │ • metadataName │ │ • foreignKey (replicated from FK constant) │ │ • inverseOneToManyProperty │ │ • isNullable │ │ • universalForeignKey │ └──────────────────────────┬────────────────────────────────────┘ │ ┌──────────────────┼──────────────────┐ │ │ │ ▼ ▼ ▼ ┌───────────────────┐ ┌────────────────┐ ┌──────────────────────┐ │ Type consumers │ │ Atomic utils │ │ Optimistic utils │ │───────────────────│ │────────────────│ │──────────────────────│ │ • JoinColumn │ │ • resolve-* │ │ • add/delete flat │ │ • RelatedNames │ │ • get-* │ │ entity maps │ │ • UniversalFlat │ │ │ │ • add/delete │ │ EntityFrom │ │ │ │ universal flat │ │ │ │ │ │ entity maps │ └───────────────────┘ └────────────────┘ │ │ │ (bridge via │ │ inverseOneToMany │ │ Property → │ │ ONE_TO_MANY for │ │ aggregator lookup) │ └──────────────────────┘ ``` ### Previously ``` ┌─────────────────────────────────────────────────────────────────────┐ │ ALL_METADATA_RELATIONS │ │─────────────────────────────────────────────────────────────────────│ │ Derived from: Entity types │ │ │ │ Structure: { [metadataName]: { manyToOne: {...}, oneToMany: {...},│ │ serializedRelations?: {...} } } │ │ │ │ manyToOne provides: │ │ • metadataName │ │ • foreignKey │ │ • flatEntityForeignKeyAggregator (nullable, often wrong/null) │ │ • isNullable │ │ │ │ oneToMany provides: │ │ • metadataName │ │ │ │ Monolithic single source of truth │ └──────────────────────────┬──────────────────────────────────────────┘ │ │ manyToOne entries transformed via │ ToUniversalMetadataManyToOneRelationConfiguration │ ▼ ┌─────────────────────────────────────────────────────────────────────┐ │ ALL_UNIVERSAL_METADATA_RELATIONS │ │─────────────────────────────────────────────────────────────────────│ │ Derived from: ALL_METADATA_RELATIONS (type-level transform) │ │ │ │ Structure: { [metadataName]: { manyToOne: {...}, oneToMany: {...} │ │ } } │ │ │ │ manyToOne provides: │ │ • metadataName │ │ • foreignKey │ │ • universalForeignKey (derived: FK → replace Id → UniversalId) │ │ • universalFlatEntityForeignKeyAggregator (derived from │ │ flatEntityForeignKeyAggregator → replace Ids → UniversalIds) │ │ • isNullable │ │ │ │ oneToMany: passthrough from ALL_METADATA_RELATIONS │ │ │ │ Duplicated monolith with universal key transforms │ └──────────────────────────┬──────────────────────────────────────────┘ │ ┌──────────────────┼──────────────────────┐ │ │ │ ▼ ▼ ▼ ┌───────────────┐ ┌────────────────────┐ ┌──────────────────────┐ │ Type consumers│ │ Atomic utils │ │ Optimistic utils │ │───────────────│ │────────────────────│ │──────────────────────│ │ • JoinColumn │ │ • resolve-entity- │ │ • add/delete flat │ │ • RelatedNames│ │ relation-univ-id │ │ entity maps │ │ • Universal │ │ (ALL_METADATA_ │ │ (ALL_METADATA_ │ │ FlatEntity │ │ RELATIONS │ │ RELATIONS │ │ From │ │ .manyToOne) │ │ .manyToOne) │ │ │ │ │ │ │ │ Mixed usage │ │ • resolve-univ- │ │ • add/delete univ │ │ of both │ │ relation-ids │ │ flat entity maps │ │ constants │ │ (ALL_UNIVERSAL_ │ │ (ALL_UNIVERSAL_ │ │ │ │ METADATA_REL │ │ METADATA_REL │ │ │ │ .manyToOne) │ │ .manyToOne) │ │ │ │ │ │ │ │ │ │ • resolve-univ- │ │ universalFlatEntity │ │ │ │ update-rel-ids │ │ ForeignKeyAggregator │ │ │ │ (ALL_UNIVERSAL_ │ │ read directly from │ │ │ │ METADATA_REL │ │ the constant │ │ │ │ .manyToOne) │ │ │ │ │ │ │ │ │ │ │ │ • regex hack: │ │ │ │ │ │ foreignKey │ │ │ │ │ │ .replace(/Id$/, │ │ │ │ │ │ 'UniversalId') │ │ │ └───────────────┘ └────────────────────┘ └──────────────────────┘ ``` --- .cursor/rules/creating-syncable-entity.mdc | 9 +- .../SKILL.md | 107 ++-- .../field-metadata/field-metadata.entity.ts | 9 +- ...configuration-by-metadata-name.constant.ts | 2 +- ...ny-to-one-metadata-foreign-key.constant.ts | 253 ++++++++ ...many-to-one-metadata-relations.constant.ts | 433 ++++++++++++++ .../all-metadata-relations.constant.ts | 537 ----------------- ...quired-metadata-for-validation.constant.ts | 2 +- ...l-metadata-serialized-relation.constant.ts | 53 ++ ...one-to-many-metadata-relations.constant.ts | 215 +++++++ ...adata-many-to-one-join-column.type-test.ts | 1 - .../metadata-many-to-one-join-column.type.ts | 4 +- ...many-to-one-related-metadata-names.type.ts | 4 +- ...one-to-many-related-metadata-names.type.ts | 4 +- ...ata-names-children-first.util.spec.ts.snap | 4 +- ...ity-maps-through-mutation-or-throw.util.ts | 54 +- ...ity-maps-through-mutation-or-throw.util.ts | 54 +- ...etadata-entity-relation-properties.util.ts | 13 +- ...metadata-many-to-one-related-names.util.ts | 6 +- ...metadata-one-to-many-related-names.util.ts | 6 +- ...metadata-serialized-relation-names.util.ts | 12 +- ...ity-relation-universal-identifiers.util.ts | 42 +- .../types/flat-object-metadata.type.ts | 2 +- .../object-metadata/object-metadata.entity.ts | 6 - ...ggregator-properties.constant.spec.ts.snap | 19 +- ...eign-key-aggregator-properties.constant.ts | 52 +- ...l-universal-metadata-relations.constant.ts | 543 ------------------ .../types/to-universal-foreign-key.type.ts | 2 + .../types/universal-flat-entity-from.type.ts | 7 +- .../universal-flat-object-metadata.type.ts | 1 - ...ity-maps-through-mutation-or-throw.util.ts | 46 +- ...ity-maps-through-mutation-or-throw.util.ts | 44 +- ...versal-relation-identifiers-to-ids.util.ts | 46 +- ...update-relation-identifiers-to-ids.util.ts | 27 +- .../get-object-metadata-entity.mock.ts | 1 - ...rkspace-migration.integration-spec.ts.snap | 4 - 36 files changed, 1279 insertions(+), 1345 deletions(-) create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-many-to-one-metadata-foreign-key.constant.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-many-to-one-metadata-relations.constant.ts delete mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-metadata-relations.constant.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-metadata-serialized-relation.constant.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-one-to-many-metadata-relations.constant.ts delete mode 100644 packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/constants/all-universal-metadata-relations.constant.ts create mode 100644 packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/to-universal-foreign-key.type.ts diff --git a/.cursor/rules/creating-syncable-entity.mdc b/.cursor/rules/creating-syncable-entity.mdc index a237a3345c8..206bc123033 100644 --- a/.cursor/rules/creating-syncable-entity.mdc +++ b/.cursor/rules/creating-syncable-entity.mdc @@ -56,7 +56,7 @@ Follow these skills in order: - Create TypeORM entity (extends `SyncableEntity`) - Define flat entity types - Define action types (universal + flat) -- Register in 4 central constants +- Register in 5 central constants **Why first:** Everything else depends on these types @@ -177,9 +177,12 @@ packages/twenty-server/src/engine/metadata-modules/ │ ├── services/ │ └── utils/ └── flat-entity/constant/ # Step 1 (central registries) + ├── all-entity-properties-configuration-by-metadata-name.constant.ts + ├── all-one-to-many-metadata-relations.constant.ts + ├── all-many-to-one-metadata-foreign-key.constant.ts + └── all-many-to-one-metadata-relations.constant.ts packages/twenty-server/src/engine/workspace-manager/workspace-migration/ -├── universal-flat-entity/constants/ # Step 1 ├── workspace-migration-builder/ # Step 3 │ ├── builders/my-entity/ │ └── validators/services/ @@ -192,7 +195,7 @@ packages/twenty-server/src/engine/workspace-manager/workspace-migration/ Before considering complete: - [ ] All 6 guides completed - [ ] TypeORM entity extends `SyncableEntity` -- [ ] All constants registered (4 central registries) +- [ ] All constants registered (5 central registries) - [ ] Cache service with correct decorator - [ ] Transform utils return universal flat entities - [ ] Validator never throws/mutates diff --git a/.cursor/skills/syncable-entity-types-and-constants/SKILL.md b/.cursor/skills/syncable-entity-types-and-constants/SKILL.md index d91de3d87f7..fc2eb1c7d4a 100644 --- a/.cursor/skills/syncable-entity-types-and-constants/SKILL.md +++ b/.cursor/skills/syncable-entity-types-and-constants/SKILL.md @@ -1,6 +1,6 @@ --- name: syncable-entity-types-and-constants -description: Define types, entities, and central constant registrations for syncable entities in Twenty's workspace migration system. Use when creating new syncable entities, defining TypeORM entities, flat entity types, or registering in central constants (ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME, ALL_METADATA_RELATIONS, ALL_UNIVERSAL_METADATA_RELATIONS). +description: Define types, entities, and central constant registrations for syncable entities in Twenty's workspace migration system. Use when creating new syncable entities, defining TypeORM entities, flat entity types, or registering in central constants (ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME, ALL_ONE_TO_MANY_METADATA_RELATIONS, ALL_MANY_TO_ONE_METADATA_FOREIGN_KEY, ALL_MANY_TO_ONE_METADATA_RELATIONS). --- # Syncable Entity: Types & Constants (Step 1/6) @@ -18,7 +18,7 @@ This step creates: 2. TypeORM entity (extends `SyncableEntity`) 3. Flat entity types 4. Action types (universal + flat) -5. Central constant registrations (4 constants) +5. Central constant registrations (5 constants) --- @@ -225,61 +225,91 @@ export const ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME = { - `toStringify: true` → JSONB/object property (needs JSON serialization) - `universalProperty` → Maps to universal version (for foreign keys & JSONB with `SerializedRelation`) -### 6c. ALL_METADATA_RELATIONS +### 6c. ALL_ONE_TO_MANY_METADATA_RELATIONS -**File**: `src/engine/metadata-modules/flat-entity/constant/all-metadata-relations.constant.ts` +**File**: `src/engine/metadata-modules/flat-entity/constant/all-one-to-many-metadata-relations.constant.ts` + +This constant is **type-checked** — values for `metadataName`, `flatEntityForeignKeyAggregator`, and `universalFlatEntityForeignKeyAggregator` are derived from entity type definitions. The aggregator names follow the pattern: remove trailing `'s'` from the relation property name, then append `Ids` or `UniversalIdentifiers`. ```typescript -export const ALL_METADATA_RELATIONS = { +export const ALL_ONE_TO_MANY_METADATA_RELATIONS = { // ... existing entries myEntity: { - manyToOne: { - workspace: null, - application: null, - parentEntity: { - metadataName: 'parentEntity', - flatEntityForeignKeyAggregator: 'myEntityIds', - foreignKey: 'parentEntityId', - isNullable: false, - }, + // If myEntity has a `childEntities: ChildEntityEntity[]` property: + childEntities: { + metadataName: 'childEntity', + flatEntityForeignKeyAggregator: 'childEntityIds', + universalFlatEntityForeignKeyAggregator: 'childEntityUniversalIdentifiers', }, - oneToMany: { - childEntities: { metadataName: 'childEntity' }, - }, - // Only if JSONB contains SerializedRelation fields - serializedRelations: { - fieldMetadata: true, + // null for relations to non-syncable entities + someNonSyncableRelation: null, + }, +} as const; +``` + +### 6d. ALL_MANY_TO_ONE_METADATA_FOREIGN_KEY + +**File**: `src/engine/metadata-modules/flat-entity/constant/all-many-to-one-metadata-foreign-key.constant.ts` + +Low-level primitive constant. Only contains `foreignKey` — the column name ending in `Id` that stores the foreign key. Type-checked against entity properties. + +```typescript +export const ALL_MANY_TO_ONE_METADATA_FOREIGN_KEY = { + // ... existing entries + myEntity: { + workspace: null, + application: null, + parentEntity: { + foreignKey: 'parentEntityId', }, }, } as const; ``` -### 6d. ALL_UNIVERSAL_METADATA_RELATIONS +### 6e. ALL_MANY_TO_ONE_METADATA_RELATIONS -**File**: `src/engine/workspace-manager/workspace-migration/universal-flat-entity/constants/all-universal-metadata-relations.constant.ts` +**File**: `src/engine/metadata-modules/flat-entity/constant/all-many-to-one-metadata-relations.constant.ts` + +Derived from both `ALL_MANY_TO_ONE_METADATA_FOREIGN_KEY` (for `foreignKey` type and `universalForeignKey` derivation) and `ALL_ONE_TO_MANY_METADATA_RELATIONS` (for `inverseOneToManyProperty` key constraint). This is the main constant consumed by utils and optimistic tooling. ```typescript -export const ALL_UNIVERSAL_METADATA_RELATIONS = { +export const ALL_MANY_TO_ONE_METADATA_RELATIONS = { // ... existing entries myEntity: { - manyToOne: { - workspace: null, - application: null, - parentEntity: { - metadataName: 'parentEntity', - foreignKey: 'parentEntityId', - universalForeignKey: 'parentEntityUniversalIdentifier', - universalFlatEntityForeignKeyAggregator: 'myEntityUniversalIdentifiers', - isNullable: false, - }, - }, - oneToMany: { - childEntities: { metadataName: 'childEntity' }, + workspace: null, + application: null, + parentEntity: { + metadataName: 'parentEntity', + foreignKey: 'parentEntityId', + inverseOneToManyProperty: 'myEntities', // key in ALL_ONE_TO_MANY_METADATA_RELATIONS['parentEntity'], or null if no inverse + isNullable: false, + universalForeignKey: 'parentEntityUniversalIdentifier', }, }, } as const; ``` +**Derivation dependency graph**: + +``` +ALL_MANY_TO_ONE_METADATA_FOREIGN_KEY ALL_ONE_TO_MANY_METADATA_RELATIONS +(foreignKey only) (metadataName, aggregators) + │ │ + │ FK type + universalFK derivation │ inverseOneToManyProperty keys + │ │ + └────────────────┬───────────────────────┘ + ▼ + ALL_MANY_TO_ONE_METADATA_RELATIONS + (metadataName, foreignKey, inverseOneToManyProperty, + isNullable, universalForeignKey) +``` + +**Rules**: +- `workspace: null`, `application: null` — always present, always null (non-syncable relations) +- `inverseOneToManyProperty` — must be a key in `ALL_ONE_TO_MANY_METADATA_RELATIONS[targetMetadataName]`, or `null` if the target entity doesn't expose an inverse one-to-many relation +- `universalForeignKey` — derived from `foreignKey` by replacing the `Id` suffix with `UniversalIdentifier` +- Optimistic utils resolve `flatEntityForeignKeyAggregator` / `universalFlatEntityForeignKeyAggregator` at runtime by looking up `inverseOneToManyProperty` in `ALL_ONE_TO_MANY_METADATA_RELATIONS` + --- ## Checklist @@ -295,8 +325,9 @@ Before moving to Step 2: - [ ] Universal and flat action types defined - [ ] Registered in `AllFlatEntityTypesByMetadataName` - [ ] Registered in `ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME` -- [ ] Registered in `ALL_METADATA_RELATIONS` -- [ ] Registered in `ALL_UNIVERSAL_METADATA_RELATIONS` +- [ ] Registered in `ALL_ONE_TO_MANY_METADATA_RELATIONS` (if entity has one-to-many relations) +- [ ] Registered in `ALL_MANY_TO_ONE_METADATA_FOREIGN_KEY` +- [ ] Registered in `ALL_MANY_TO_ONE_METADATA_RELATIONS` - [ ] TypeScript compiles without errors --- diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.entity.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.entity.ts index 75cc0466afd..cfbc818ef3d 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.entity.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.entity.ts @@ -151,11 +151,10 @@ export class FieldMetadataEntity< TFieldMetadataType >; - @ManyToOne( - () => ObjectMetadataEntity, - (objectMetadata) => objectMetadata.targetRelationFields, - { onDelete: 'CASCADE', nullable: true }, - ) + @ManyToOne(() => ObjectMetadataEntity, { + onDelete: 'CASCADE', + nullable: true, + }) @JoinColumn({ name: 'relationTargetObjectMetadataId' }) relationTargetObjectMetadata: AssignTypeIfIsMorphOrRelationFieldMetadataType< Relation, diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-entity-properties-configuration-by-metadata-name.constant.ts b/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-entity-properties-configuration-by-metadata-name.constant.ts index a49157dd295..a48ffa68a5e 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-entity-properties-configuration-by-metadata-name.constant.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-entity-properties-configuration-by-metadata-name.constant.ts @@ -4,8 +4,8 @@ import { type MetadataEntity } from 'src/engine/metadata-modules/flat-entity/typ import { type MetadataManyToOneJoinColumn } from 'src/engine/metadata-modules/flat-entity/types/metadata-many-to-one-join-column.type'; import { type ScalarFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/scalar-flat-entity.type'; import { type AllJsonbPropertiesWithSerializedPropertiesForMetadataName } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/constants/all-jsonb-properties-with-serialized-relation-by-metadata-name.constant'; -import { type ToUniversalForeignKey } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/constants/all-universal-metadata-relations.constant'; import { type ExtractJsonbProperties } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/extract-jsonb-properties.type'; +import { type ToUniversalForeignKey } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/to-universal-foreign-key.type'; type HasObjectInUnion = T extends unknown ? T extends object diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-many-to-one-metadata-foreign-key.constant.ts b/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-many-to-one-metadata-foreign-key.constant.ts new file mode 100644 index 00000000000..f6452789c2a --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-many-to-one-metadata-foreign-key.constant.ts @@ -0,0 +1,253 @@ +import { type AllMetadataName } from 'twenty-shared/metadata'; +import { type Expect } from 'twenty-shared/testing'; +import { type ExtractPropertiesThatEndsWithId } from 'twenty-shared/types'; +import { type Relation } from 'typeorm'; + +import { type ExtractEntityManyToOneEntityRelationProperties } from 'src/engine/metadata-modules/flat-entity/types/extract-entity-many-to-one-entity-relation-properties.type'; +import { type MetadataEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-entity.type'; +import { type SyncableEntity } from 'src/engine/workspace-manager/types/syncable-entity.interface'; + +type ManyToOneRelationConfiguration< + TSourceMetadataName extends AllMetadataName, + TRelationProperty extends ExtractEntityManyToOneEntityRelationProperties< + MetadataEntity + >, +> = + NonNullable< + MetadataEntity[TRelationProperty] + > extends Relation + ? { + foreignKey: ExtractPropertiesThatEndsWithId< + MetadataEntity, + 'id' | 'workspaceId' + >; + } + : null; + +type ManyToOneMetadataRelationsProperties = { + [TSourceMetadataName in AllMetadataName]: { + [TRelationProperty in ExtractEntityManyToOneEntityRelationProperties< + MetadataEntity + >]: ManyToOneRelationConfiguration; + }; +}; + +export const ALL_MANY_TO_ONE_METADATA_FOREIGN_KEY = { + agent: { + workspace: null, + application: null, + }, + skill: { + workspace: null, + application: null, + }, + commandMenuItem: { + workspace: null, + application: null, + availabilityObjectMetadata: { + foreignKey: 'availabilityObjectMetadataId', + }, + frontComponent: { + foreignKey: 'frontComponentId', + }, + }, + navigationMenuItem: { + workspace: null, + userWorkspace: null, + application: null, + targetObjectMetadata: { + foreignKey: 'targetObjectMetadataId', + }, + folder: { + foreignKey: 'folderId', + }, + view: { + foreignKey: 'viewId', + }, + }, + fieldMetadata: { + object: { + foreignKey: 'objectMetadataId', + }, + workspace: null, + application: null, + relationTargetFieldMetadata: { + foreignKey: 'relationTargetFieldMetadataId', + }, + relationTargetObjectMetadata: { + foreignKey: 'relationTargetObjectMetadataId', + }, + }, + objectMetadata: { + dataSource: null, + workspace: null, + application: null, + }, + view: { + objectMetadata: { + foreignKey: 'objectMetadataId', + }, + workspace: null, + createdBy: null, + application: null, + calendarFieldMetadata: { + foreignKey: 'calendarFieldMetadataId', + }, + kanbanAggregateOperationFieldMetadata: { + foreignKey: 'kanbanAggregateOperationFieldMetadataId', + }, + mainGroupByFieldMetadata: { + foreignKey: 'mainGroupByFieldMetadataId', + }, + }, + viewField: { + fieldMetadata: { + foreignKey: 'fieldMetadataId', + }, + view: { + foreignKey: 'viewId', + }, + viewFieldGroup: { + foreignKey: 'viewFieldGroupId', + }, + workspace: null, + application: null, + }, + viewFieldGroup: { + view: { + foreignKey: 'viewId', + }, + workspace: null, + application: null, + }, + viewFilter: { + fieldMetadata: { + foreignKey: 'fieldMetadataId', + }, + view: { + foreignKey: 'viewId', + }, + viewFilterGroup: { + foreignKey: 'viewFilterGroupId', + }, + workspace: null, + application: null, + }, + viewGroup: { + view: { + foreignKey: 'viewId', + }, + workspace: null, + application: null, + }, + index: { + objectMetadata: { + foreignKey: 'objectMetadataId', + }, + workspace: null, + application: null, + }, + logicFunction: { + workspace: null, + application: null, + }, + role: { + workspace: null, + application: null, + }, + roleTarget: { + role: { + foreignKey: 'roleId', + }, + apiKey: null, + workspace: null, + application: null, + }, + pageLayout: { + workspace: null, + objectMetadata: { + foreignKey: 'objectMetadataId', + }, + application: null, + defaultTabToFocusOnMobileAndSidePanel: { + foreignKey: 'defaultTabToFocusOnMobileAndSidePanelId', + }, + }, + pageLayoutTab: { + workspace: null, + pageLayout: { + foreignKey: 'pageLayoutId', + }, + application: null, + }, + pageLayoutWidget: { + workspace: null, + pageLayoutTab: { + foreignKey: 'pageLayoutTabId', + }, + objectMetadata: { + foreignKey: 'objectMetadataId', + }, + application: null, + }, + rowLevelPermissionPredicate: { + workspace: null, + role: { + foreignKey: 'roleId', + }, + fieldMetadata: { + foreignKey: 'fieldMetadataId', + }, + workspaceMemberFieldMetadata: { + foreignKey: 'workspaceMemberFieldMetadataId', + }, + objectMetadata: { + foreignKey: 'objectMetadataId', + }, + rowLevelPermissionPredicateGroup: { + foreignKey: 'rowLevelPermissionPredicateGroupId', + }, + application: null, + }, + rowLevelPermissionPredicateGroup: { + objectMetadata: { + foreignKey: 'objectMetadataId', + }, + role: { + foreignKey: 'roleId', + }, + parentRowLevelPermissionPredicateGroup: { + foreignKey: 'parentRowLevelPermissionPredicateGroupId', + }, + workspace: null, + application: null, + }, + viewFilterGroup: { + application: null, + parentViewFilterGroup: { + foreignKey: 'parentViewFilterGroupId', + }, + view: { + foreignKey: 'viewId', + }, + workspace: null, + }, + frontComponent: { + workspace: null, + application: null, + }, + webhook: { + workspace: null, + application: null, + }, +} as const satisfies ManyToOneMetadataRelationsProperties; + +// satisfies with complex mapped types involving nested generics doesn't always catch missing required keys +// eslint-disable-next-line unused-imports/no-unused-vars +type Assertions = [ + Expect< + AllMetadataName extends keyof typeof ALL_MANY_TO_ONE_METADATA_FOREIGN_KEY + ? true + : false + >, +]; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-many-to-one-metadata-relations.constant.ts b/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-many-to-one-metadata-relations.constant.ts new file mode 100644 index 00000000000..aa65e74529d --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-many-to-one-metadata-relations.constant.ts @@ -0,0 +1,433 @@ +import { type AllMetadataName } from 'twenty-shared/metadata'; +import { type Expect } from 'twenty-shared/testing'; +import { type Relation } from 'typeorm'; + +import { type ALL_MANY_TO_ONE_METADATA_FOREIGN_KEY } from 'src/engine/metadata-modules/flat-entity/constant/all-many-to-one-metadata-foreign-key.constant'; +import { type ALL_ONE_TO_MANY_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-one-to-many-metadata-relations.constant'; +import { type ExtractEntityManyToOneEntityRelationProperties } from 'src/engine/metadata-modules/flat-entity/types/extract-entity-many-to-one-entity-relation-properties.type'; +import { type FromMetadataEntityToMetadataName } from 'src/engine/metadata-modules/flat-entity/types/from-metadata-entity-to-metadata-name.type'; +import { type MetadataEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-entity.type'; +import { type SyncableEntity } from 'src/engine/workspace-manager/types/syncable-entity.interface'; +import { type ToUniversalForeignKey } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/to-universal-foreign-key.type'; + +type FromRelationPropertyToForeignKey< + TMetadataName extends AllMetadataName, + TRelationProperty extends PropertyKey, +> = TRelationProperty extends keyof (typeof ALL_MANY_TO_ONE_METADATA_FOREIGN_KEY)[TMetadataName] + ? (typeof ALL_MANY_TO_ONE_METADATA_FOREIGN_KEY)[TMetadataName][TRelationProperty] extends { + foreignKey: infer FK extends string; + } + ? FK + : never + : never; + +type ManyToOneRelationValue< + TSourceMetadataName extends AllMetadataName, + TRelationProperty extends ExtractEntityManyToOneEntityRelationProperties< + MetadataEntity + >, +> = + NonNullable< + MetadataEntity[TRelationProperty] + > extends Relation + ? FromMetadataEntityToMetadataName extends infer TTargetMetadataName extends + AllMetadataName + ? FromRelationPropertyToForeignKey< + TSourceMetadataName, + TRelationProperty + > extends infer FK + ? { + metadataName: TTargetMetadataName; + foreignKey: FK; + // Should not be nullable, in the best of the world relation should always describe an inverse property + inverseOneToManyProperty: + | keyof (typeof ALL_ONE_TO_MANY_METADATA_RELATIONS)[TTargetMetadataName] + | null; + isNullable: null extends MetadataEntity[TRelationProperty] + ? true + : false; + universalForeignKey: ToUniversalForeignKey; + } + : never + : never + : null; + +type ManyToOneMetadataRelationsProperties = { + [TSourceMetadataName in AllMetadataName]: { + [TRelationProperty in ExtractEntityManyToOneEntityRelationProperties< + MetadataEntity + >]: ManyToOneRelationValue; + }; +}; + +export const ALL_MANY_TO_ONE_METADATA_RELATIONS = { + agent: { + workspace: null, + application: null, + }, + skill: { + workspace: null, + application: null, + }, + commandMenuItem: { + workspace: null, + application: null, + availabilityObjectMetadata: { + metadataName: 'objectMetadata', + foreignKey: 'availabilityObjectMetadataId', + inverseOneToManyProperty: null, + isNullable: true, + universalForeignKey: 'availabilityObjectMetadataUniversalIdentifier', + }, + frontComponent: { + metadataName: 'frontComponent', + foreignKey: 'frontComponentId', + inverseOneToManyProperty: null, + isNullable: true, + universalForeignKey: 'frontComponentUniversalIdentifier', + }, + }, + navigationMenuItem: { + workspace: null, + userWorkspace: null, + application: null, + targetObjectMetadata: { + metadataName: 'objectMetadata', + foreignKey: 'targetObjectMetadataId', + inverseOneToManyProperty: null, + isNullable: true, + universalForeignKey: 'targetObjectMetadataUniversalIdentifier', + }, + folder: { + metadataName: 'navigationMenuItem', + foreignKey: 'folderId', + inverseOneToManyProperty: null, + isNullable: true, + universalForeignKey: 'folderUniversalIdentifier', + }, + view: { + metadataName: 'view', + foreignKey: 'viewId', + inverseOneToManyProperty: null, + isNullable: true, + universalForeignKey: 'viewUniversalIdentifier', + }, + }, + fieldMetadata: { + object: { + metadataName: 'objectMetadata', + foreignKey: 'objectMetadataId', + inverseOneToManyProperty: 'fields', + isNullable: false, + universalForeignKey: 'objectMetadataUniversalIdentifier', + }, + workspace: null, + application: null, + relationTargetFieldMetadata: { + metadataName: 'fieldMetadata', + foreignKey: 'relationTargetFieldMetadataId', + inverseOneToManyProperty: null, + isNullable: true, + universalForeignKey: 'relationTargetFieldMetadataUniversalIdentifier', + }, + relationTargetObjectMetadata: { + metadataName: 'objectMetadata', + foreignKey: 'relationTargetObjectMetadataId', + inverseOneToManyProperty: null, + isNullable: true, + universalForeignKey: 'relationTargetObjectMetadataUniversalIdentifier', + }, + }, + objectMetadata: { + dataSource: null, + workspace: null, + application: null, + }, + view: { + objectMetadata: { + metadataName: 'objectMetadata', + foreignKey: 'objectMetadataId', + inverseOneToManyProperty: 'views', + isNullable: false, + universalForeignKey: 'objectMetadataUniversalIdentifier', + }, + workspace: null, + createdBy: null, + application: null, + calendarFieldMetadata: { + metadataName: 'fieldMetadata', + foreignKey: 'calendarFieldMetadataId', + inverseOneToManyProperty: 'calendarViews', + isNullable: true, + universalForeignKey: 'calendarFieldMetadataUniversalIdentifier', + }, + kanbanAggregateOperationFieldMetadata: { + metadataName: 'fieldMetadata', + foreignKey: 'kanbanAggregateOperationFieldMetadataId', + inverseOneToManyProperty: 'kanbanAggregateOperationViews', + isNullable: true, + universalForeignKey: + 'kanbanAggregateOperationFieldMetadataUniversalIdentifier', + }, + mainGroupByFieldMetadata: { + metadataName: 'fieldMetadata', + foreignKey: 'mainGroupByFieldMetadataId', + inverseOneToManyProperty: 'mainGroupByFieldMetadataViews', + isNullable: true, + universalForeignKey: 'mainGroupByFieldMetadataUniversalIdentifier', + }, + }, + viewField: { + fieldMetadata: { + metadataName: 'fieldMetadata', + foreignKey: 'fieldMetadataId', + inverseOneToManyProperty: 'viewFields', + isNullable: false, + universalForeignKey: 'fieldMetadataUniversalIdentifier', + }, + view: { + metadataName: 'view', + foreignKey: 'viewId', + inverseOneToManyProperty: 'viewFields', + isNullable: false, + universalForeignKey: 'viewUniversalIdentifier', + }, + viewFieldGroup: { + metadataName: 'viewFieldGroup', + foreignKey: 'viewFieldGroupId', + inverseOneToManyProperty: 'viewFields', + isNullable: true, + universalForeignKey: 'viewFieldGroupUniversalIdentifier', + }, + workspace: null, + application: null, + }, + viewFieldGroup: { + view: { + metadataName: 'view', + foreignKey: 'viewId', + inverseOneToManyProperty: 'viewFieldGroups', + isNullable: false, + universalForeignKey: 'viewUniversalIdentifier', + }, + workspace: null, + application: null, + }, + viewFilter: { + fieldMetadata: { + metadataName: 'fieldMetadata', + foreignKey: 'fieldMetadataId', + inverseOneToManyProperty: 'viewFilters', + isNullable: false, + universalForeignKey: 'fieldMetadataUniversalIdentifier', + }, + view: { + metadataName: 'view', + foreignKey: 'viewId', + inverseOneToManyProperty: 'viewFilters', + isNullable: false, + universalForeignKey: 'viewUniversalIdentifier', + }, + viewFilterGroup: { + metadataName: 'viewFilterGroup', + foreignKey: 'viewFilterGroupId', + inverseOneToManyProperty: 'viewFilters', + isNullable: true, + universalForeignKey: 'viewFilterGroupUniversalIdentifier', + }, + workspace: null, + application: null, + }, + viewGroup: { + view: { + metadataName: 'view', + foreignKey: 'viewId', + inverseOneToManyProperty: 'viewGroups', + isNullable: false, + universalForeignKey: 'viewUniversalIdentifier', + }, + workspace: null, + application: null, + }, + index: { + objectMetadata: { + metadataName: 'objectMetadata', + foreignKey: 'objectMetadataId', + inverseOneToManyProperty: 'indexMetadatas', + isNullable: false, + universalForeignKey: 'objectMetadataUniversalIdentifier', + }, + workspace: null, + application: null, + }, + logicFunction: { + workspace: null, + application: null, + }, + role: { + workspace: null, + application: null, + }, + roleTarget: { + role: { + metadataName: 'role', + foreignKey: 'roleId', + inverseOneToManyProperty: 'roleTargets', + isNullable: false, + universalForeignKey: 'roleUniversalIdentifier', + }, + apiKey: null, + workspace: null, + application: null, + }, + pageLayout: { + workspace: null, + objectMetadata: { + metadataName: 'objectMetadata', + foreignKey: 'objectMetadataId', + inverseOneToManyProperty: null, + isNullable: true, + universalForeignKey: 'objectMetadataUniversalIdentifier', + }, + application: null, + defaultTabToFocusOnMobileAndSidePanel: { + metadataName: 'pageLayoutTab', + foreignKey: 'defaultTabToFocusOnMobileAndSidePanelId', + inverseOneToManyProperty: null, + isNullable: true, + universalForeignKey: + 'defaultTabToFocusOnMobileAndSidePanelUniversalIdentifier', + }, + }, + pageLayoutTab: { + workspace: null, + pageLayout: { + metadataName: 'pageLayout', + foreignKey: 'pageLayoutId', + inverseOneToManyProperty: 'tabs', + isNullable: false, + universalForeignKey: 'pageLayoutUniversalIdentifier', + }, + application: null, + }, + pageLayoutWidget: { + workspace: null, + pageLayoutTab: { + metadataName: 'pageLayoutTab', + foreignKey: 'pageLayoutTabId', + inverseOneToManyProperty: 'widgets', + isNullable: false, + universalForeignKey: 'pageLayoutTabUniversalIdentifier', + }, + objectMetadata: { + metadataName: 'objectMetadata', + foreignKey: 'objectMetadataId', + inverseOneToManyProperty: null, + isNullable: true, + universalForeignKey: 'objectMetadataUniversalIdentifier', + }, + application: null, + }, + rowLevelPermissionPredicate: { + workspace: null, + role: { + metadataName: 'role', + foreignKey: 'roleId', + inverseOneToManyProperty: 'rowLevelPermissionPredicates', + isNullable: false, + universalForeignKey: 'roleUniversalIdentifier', + }, + fieldMetadata: { + metadataName: 'fieldMetadata', + foreignKey: 'fieldMetadataId', + inverseOneToManyProperty: null, + isNullable: false, + universalForeignKey: 'fieldMetadataUniversalIdentifier', + }, + workspaceMemberFieldMetadata: { + metadataName: 'fieldMetadata', + foreignKey: 'workspaceMemberFieldMetadataId', + inverseOneToManyProperty: null, + isNullable: true, + universalForeignKey: 'workspaceMemberFieldMetadataUniversalIdentifier', + }, + objectMetadata: { + metadataName: 'objectMetadata', + foreignKey: 'objectMetadataId', + inverseOneToManyProperty: null, + isNullable: false, + universalForeignKey: 'objectMetadataUniversalIdentifier', + }, + rowLevelPermissionPredicateGroup: { + metadataName: 'rowLevelPermissionPredicateGroup', + foreignKey: 'rowLevelPermissionPredicateGroupId', + inverseOneToManyProperty: 'rowLevelPermissionPredicates', + isNullable: true, + universalForeignKey: + 'rowLevelPermissionPredicateGroupUniversalIdentifier', + }, + application: null, + }, + rowLevelPermissionPredicateGroup: { + objectMetadata: { + metadataName: 'objectMetadata', + foreignKey: 'objectMetadataId', + inverseOneToManyProperty: null, + isNullable: false, + universalForeignKey: 'objectMetadataUniversalIdentifier', + }, + role: { + metadataName: 'role', + foreignKey: 'roleId', + inverseOneToManyProperty: 'rowLevelPermissionPredicateGroups', + isNullable: false, + universalForeignKey: 'roleUniversalIdentifier', + }, + parentRowLevelPermissionPredicateGroup: { + metadataName: 'rowLevelPermissionPredicateGroup', + foreignKey: 'parentRowLevelPermissionPredicateGroupId', + inverseOneToManyProperty: 'childRowLevelPermissionPredicateGroups', + isNullable: true, + universalForeignKey: + 'parentRowLevelPermissionPredicateGroupUniversalIdentifier', + }, + workspace: null, + application: null, + }, + viewFilterGroup: { + application: null, + parentViewFilterGroup: { + metadataName: 'viewFilterGroup', + foreignKey: 'parentViewFilterGroupId', + inverseOneToManyProperty: 'childViewFilterGroups', + isNullable: true, + universalForeignKey: 'parentViewFilterGroupUniversalIdentifier', + }, + view: { + metadataName: 'view', + foreignKey: 'viewId', + inverseOneToManyProperty: 'viewFilterGroups', + isNullable: false, + universalForeignKey: 'viewUniversalIdentifier', + }, + workspace: null, + }, + frontComponent: { + workspace: null, + application: null, + }, + webhook: { + workspace: null, + application: null, + }, +} as const satisfies ManyToOneMetadataRelationsProperties; + +// satisfies with complex mapped types involving nested generics doesn't always catch missing required keys +// eslint-disable-next-line unused-imports/no-unused-vars +type Assertions = [ + Expect< + AllMetadataName extends keyof typeof ALL_MANY_TO_ONE_METADATA_RELATIONS + ? true + : false + >, +]; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-metadata-relations.constant.ts b/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-metadata-relations.constant.ts deleted file mode 100644 index fd78e4367a1..00000000000 --- a/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-metadata-relations.constant.ts +++ /dev/null @@ -1,537 +0,0 @@ -import { type AllMetadataName } from 'twenty-shared/metadata'; -import { type Expect } from 'twenty-shared/testing'; -import { type ExtractPropertiesThatEndsWithId } from 'twenty-shared/types'; -import { type Relation } from 'typeorm'; - -import { type AddSuffixToEntityOneToManyProperties } from 'src/engine/metadata-modules/flat-entity/types/add-suffix-to-entity-one-to-many-properties.type'; -import { type ExtractEntityManyToOneEntityRelationProperties } from 'src/engine/metadata-modules/flat-entity/types/extract-entity-many-to-one-entity-relation-properties.type'; -import { type ExtractEntityOneToManyEntityRelationProperties } from 'src/engine/metadata-modules/flat-entity/types/extract-entity-one-to-many-entity-relation-properties.type'; -import { type FromMetadataEntityToMetadataName } from 'src/engine/metadata-modules/flat-entity/types/from-metadata-entity-to-metadata-name.type'; -import { type MetadataEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-entity.type'; -import { type SyncableEntity } from 'src/engine/workspace-manager/types/syncable-entity.interface'; -import { type AllJsonbPropertiesWithSerializedPropertiesForMetadataName } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/constants/all-jsonb-properties-with-serialized-relation-by-metadata-name.constant'; - -export type MetadataManyToOneRelationConfiguration< - TSourceMetadataName extends AllMetadataName, - TRelationProperty extends ExtractEntityManyToOneEntityRelationProperties< - MetadataEntity - >, -> = - NonNullable< - MetadataEntity[TRelationProperty] - > extends Relation - ? { - metadataName: FromMetadataEntityToMetadataName; - flatEntityForeignKeyAggregator: - | keyof AddSuffixToEntityOneToManyProperties - | null; - foreignKey: ExtractPropertiesThatEndsWithId< - MetadataEntity, - 'id' | 'workspaceId' - >; - isNullable: null extends MetadataEntity[TRelationProperty] - ? true - : false; - } - : // Note: In the best of the world should not be nullable, entities should always declare inverside keys - null; - -type OneToManyRelationValue< - TSourceMetadataName extends AllMetadataName, - TRelationProperty extends ExtractEntityOneToManyEntityRelationProperties< - MetadataEntity - >, -> = MetadataEntity[TRelationProperty] extends (infer TTargetEntity extends - SyncableEntity)[] - ? { - metadataName: FromMetadataEntityToMetadataName; - } - : null; - -type MetadataRelationsProperties = { - [TSourceMetadataName in AllMetadataName]: { - manyToOne: { - [TRelationProperty in ExtractEntityManyToOneEntityRelationProperties< - MetadataEntity - >]: MetadataManyToOneRelationConfiguration< - TSourceMetadataName, - TRelationProperty - >; - }; - oneToMany: { - [TRelationProperty in ExtractEntityOneToManyEntityRelationProperties< - MetadataEntity - >]: OneToManyRelationValue; - }; - } & ([ - AllJsonbPropertiesWithSerializedPropertiesForMetadataName, - ] extends [never] - ? // eslint-disable-next-line @typescript-eslint/no-empty-object-type - {} - : { - serializedRelations: Partial>; - }); -}; - -export const ALL_METADATA_RELATIONS = { - agent: { - manyToOne: { - workspace: null, - application: null, - }, - oneToMany: {}, - }, - skill: { - manyToOne: { - workspace: null, - application: null, - }, - oneToMany: {}, - }, - commandMenuItem: { - manyToOne: { - workspace: null, - application: null, - availabilityObjectMetadata: { - metadataName: 'objectMetadata', - flatEntityForeignKeyAggregator: null, - foreignKey: 'availabilityObjectMetadataId', - isNullable: true, - }, - frontComponent: { - metadataName: 'frontComponent', - flatEntityForeignKeyAggregator: null, - foreignKey: 'frontComponentId', - isNullable: true, - }, - }, - oneToMany: {}, - }, - navigationMenuItem: { - manyToOne: { - workspace: null, - userWorkspace: null, - application: null, - targetObjectMetadata: { - metadataName: 'objectMetadata', - flatEntityForeignKeyAggregator: null, - foreignKey: 'targetObjectMetadataId', - isNullable: true, - }, - folder: { - metadataName: 'navigationMenuItem', - flatEntityForeignKeyAggregator: null, - foreignKey: 'folderId', - isNullable: true, - }, - view: { - metadataName: 'view', - flatEntityForeignKeyAggregator: null, - foreignKey: 'viewId', - isNullable: true, - }, - }, - oneToMany: {}, - }, - fieldMetadata: { - manyToOne: { - object: { - metadataName: 'objectMetadata', - flatEntityForeignKeyAggregator: 'fieldIds', - foreignKey: 'objectMetadataId', - isNullable: false, - }, - workspace: null, - application: null, - relationTargetFieldMetadata: { - metadataName: 'fieldMetadata', - flatEntityForeignKeyAggregator: null, - foreignKey: 'relationTargetFieldMetadataId', - isNullable: true, - }, - relationTargetObjectMetadata: { - metadataName: 'objectMetadata', - flatEntityForeignKeyAggregator: null, - foreignKey: 'relationTargetObjectMetadataId', - isNullable: true, - }, - }, - oneToMany: { - fieldPermissions: null, - indexFieldMetadatas: null, - viewFields: { metadataName: 'viewField' }, - viewFilters: { metadataName: 'viewFilter' }, - kanbanAggregateOperationViews: { metadataName: 'view' }, - calendarViews: { metadataName: 'view' }, - mainGroupByFieldMetadataViews: { metadataName: 'view' }, - }, - serializedRelations: { - fieldMetadata: true, - }, - }, - objectMetadata: { - manyToOne: { - dataSource: null, - workspace: null, - application: null, - }, - oneToMany: { - fields: { metadataName: 'fieldMetadata' }, - indexMetadatas: { metadataName: 'index' }, - targetRelationFields: { metadataName: 'fieldMetadata' }, - objectPermissions: null, - fieldPermissions: null, - views: { metadataName: 'view' }, - }, - }, - view: { - manyToOne: { - objectMetadata: { - foreignKey: 'objectMetadataId', - metadataName: 'objectMetadata', - flatEntityForeignKeyAggregator: 'viewIds', - isNullable: false, - }, - workspace: null, - createdBy: null, - application: null, - calendarFieldMetadata: { - foreignKey: 'calendarFieldMetadataId', - metadataName: 'fieldMetadata', - flatEntityForeignKeyAggregator: 'calendarViewIds', - isNullable: true, - }, - kanbanAggregateOperationFieldMetadata: { - foreignKey: 'kanbanAggregateOperationFieldMetadataId', - metadataName: 'fieldMetadata', - flatEntityForeignKeyAggregator: 'kanbanAggregateOperationViewIds', - isNullable: true, - }, - mainGroupByFieldMetadata: { - foreignKey: 'mainGroupByFieldMetadataId', - metadataName: 'fieldMetadata', - flatEntityForeignKeyAggregator: 'mainGroupByFieldMetadataViewIds', - isNullable: true, - }, - }, - oneToMany: { - viewFields: { metadataName: 'viewField' }, - viewFilters: { metadataName: 'viewFilter' }, - viewFilterGroups: { - metadataName: 'viewFilterGroup', - }, - viewGroups: { metadataName: 'viewGroup' }, - viewFieldGroups: { metadataName: 'viewFieldGroup' }, - // @ts-expect-error TODO migrate viewSort to v2 - viewSorts: null, - }, - }, - viewField: { - manyToOne: { - fieldMetadata: { - metadataName: 'fieldMetadata', - flatEntityForeignKeyAggregator: 'viewFieldIds', - foreignKey: 'fieldMetadataId', - isNullable: false, - }, - view: { - metadataName: 'view', - flatEntityForeignKeyAggregator: 'viewFieldIds', - foreignKey: 'viewId', - isNullable: false, - }, - viewFieldGroup: { - metadataName: 'viewFieldGroup', - flatEntityForeignKeyAggregator: 'viewFieldIds', - foreignKey: 'viewFieldGroupId', - isNullable: true, - }, - workspace: null, - application: null, - }, - oneToMany: {}, - }, - viewFieldGroup: { - manyToOne: { - view: { - metadataName: 'view', - flatEntityForeignKeyAggregator: 'viewFieldGroupIds', - foreignKey: 'viewId', - isNullable: false, - }, - workspace: null, - application: null, - }, - oneToMany: { - viewFields: { metadataName: 'viewField' }, - }, - }, - viewFilter: { - manyToOne: { - fieldMetadata: { - metadataName: 'fieldMetadata', - flatEntityForeignKeyAggregator: 'viewFilterIds', - foreignKey: 'fieldMetadataId', - isNullable: false, - }, - view: { - metadataName: 'view', - flatEntityForeignKeyAggregator: 'viewFilterIds', - foreignKey: 'viewId', - isNullable: false, - }, - viewFilterGroup: { - flatEntityForeignKeyAggregator: 'viewFilterIds', - foreignKey: 'viewFilterGroupId', - metadataName: 'viewFilterGroup', - isNullable: true, - }, - workspace: null, - application: null, - }, - oneToMany: {}, - }, - viewGroup: { - manyToOne: { - view: { - metadataName: 'view', - flatEntityForeignKeyAggregator: 'viewGroupIds', - foreignKey: 'viewId', - isNullable: false, - }, - workspace: null, - application: null, - }, - oneToMany: {}, - }, - index: { - manyToOne: { - objectMetadata: { - metadataName: 'objectMetadata', - flatEntityForeignKeyAggregator: 'indexMetadataIds', - foreignKey: 'objectMetadataId', - isNullable: false, - }, - workspace: null, - application: null, - }, - oneToMany: { - indexFieldMetadatas: null, - }, - }, - logicFunction: { - manyToOne: { - workspace: null, - application: null, - }, - oneToMany: {}, - }, - role: { - manyToOne: { - workspace: null, - application: null, - }, - oneToMany: { - roleTargets: { metadataName: 'roleTarget' }, - objectPermissions: null, - permissionFlags: null, - fieldPermissions: null, - rowLevelPermissionPredicates: { - metadataName: 'rowLevelPermissionPredicate', - }, - rowLevelPermissionPredicateGroups: { - metadataName: 'rowLevelPermissionPredicateGroup', - }, - }, - }, - roleTarget: { - manyToOne: { - role: { - metadataName: 'role', - flatEntityForeignKeyAggregator: 'roleTargetIds', - foreignKey: 'roleId', - isNullable: false, - }, - apiKey: null, - workspace: null, - application: null, - }, - oneToMany: {}, - }, - pageLayout: { - manyToOne: { - workspace: null, - objectMetadata: { - metadataName: 'objectMetadata', - flatEntityForeignKeyAggregator: null, - foreignKey: 'objectMetadataId', - isNullable: true, - }, - application: null, - defaultTabToFocusOnMobileAndSidePanel: { - metadataName: 'pageLayoutTab', - flatEntityForeignKeyAggregator: null, - foreignKey: 'defaultTabToFocusOnMobileAndSidePanelId', - isNullable: true, - }, - }, - oneToMany: { - tabs: { metadataName: 'pageLayoutTab' }, - }, - }, - pageLayoutTab: { - manyToOne: { - workspace: null, - pageLayout: { - metadataName: 'pageLayout', - flatEntityForeignKeyAggregator: 'tabIds', - foreignKey: 'pageLayoutId', - isNullable: false, - }, - application: null, - }, - oneToMany: { - widgets: { metadataName: 'pageLayoutWidget' }, - }, - }, - pageLayoutWidget: { - manyToOne: { - workspace: null, - pageLayoutTab: { - metadataName: 'pageLayoutTab', - flatEntityForeignKeyAggregator: 'widgetIds', - foreignKey: 'pageLayoutTabId', - isNullable: false, - }, - objectMetadata: { - metadataName: 'objectMetadata', - flatEntityForeignKeyAggregator: null, - foreignKey: 'objectMetadataId', - isNullable: true, - }, - application: null, - }, - oneToMany: {}, - serializedRelations: { - fieldMetadata: true, - }, - }, - rowLevelPermissionPredicate: { - manyToOne: { - workspace: null, - role: { - metadataName: 'role', - flatEntityForeignKeyAggregator: null, - foreignKey: 'roleId', - isNullable: false, - }, - fieldMetadata: { - metadataName: 'fieldMetadata', - flatEntityForeignKeyAggregator: null, - foreignKey: 'fieldMetadataId', - isNullable: false, - }, - workspaceMemberFieldMetadata: { - metadataName: 'fieldMetadata', - flatEntityForeignKeyAggregator: null, - foreignKey: 'workspaceMemberFieldMetadataId', - isNullable: true, - }, - objectMetadata: { - metadataName: 'objectMetadata', - flatEntityForeignKeyAggregator: null, - foreignKey: 'objectMetadataId', - isNullable: false, - }, - rowLevelPermissionPredicateGroup: { - metadataName: 'rowLevelPermissionPredicateGroup', - flatEntityForeignKeyAggregator: null, - foreignKey: 'rowLevelPermissionPredicateGroupId', - isNullable: true, - }, - application: null, - }, - oneToMany: {}, - }, - rowLevelPermissionPredicateGroup: { - manyToOne: { - objectMetadata: { - metadataName: 'objectMetadata', - flatEntityForeignKeyAggregator: null, - foreignKey: 'objectMetadataId', - isNullable: false, - }, - role: { - metadataName: 'role', - foreignKey: 'roleId', - flatEntityForeignKeyAggregator: null, - isNullable: false, - }, - parentRowLevelPermissionPredicateGroup: { - metadataName: 'rowLevelPermissionPredicateGroup', - foreignKey: 'parentRowLevelPermissionPredicateGroupId', - flatEntityForeignKeyAggregator: - 'childRowLevelPermissionPredicateGroupIds', - isNullable: true, - }, - workspace: null, - application: null, - }, - oneToMany: { - childRowLevelPermissionPredicateGroups: { - metadataName: 'rowLevelPermissionPredicateGroup', - }, - rowLevelPermissionPredicates: { - metadataName: 'rowLevelPermissionPredicate', - }, - }, - }, - viewFilterGroup: { - manyToOne: { - application: null, - parentViewFilterGroup: { - flatEntityForeignKeyAggregator: 'childViewFilterGroupIds', - foreignKey: 'parentViewFilterGroupId', - metadataName: 'viewFilterGroup', - isNullable: true, - }, - view: { - metadataName: 'view', - flatEntityForeignKeyAggregator: 'viewFilterGroupIds', - foreignKey: 'viewId', - isNullable: false, - }, - workspace: null, - }, - oneToMany: { - childViewFilterGroups: { - metadataName: 'viewFilterGroup', - }, - viewFilters: { - metadataName: 'viewFilter', - }, - }, - }, - frontComponent: { - manyToOne: { - workspace: null, - application: null, - }, - oneToMany: {}, - }, - webhook: { - manyToOne: { - workspace: null, - application: null, - }, - oneToMany: {}, - }, -} as const satisfies MetadataRelationsProperties; - -// Note: satisfies with complex mapped types involving nested generics doesn't always catch missing required keys -// eslint-disable-next-line unused-imports/no-unused-vars -type Assertions = [ - Expect< - AllMetadataName extends keyof typeof ALL_METADATA_RELATIONS ? true : false - >, -]; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-metadata-required-metadata-for-validation.constant.ts b/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-metadata-required-metadata-for-validation.constant.ts index a47821be503..2e251d60e17 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-metadata-required-metadata-for-validation.constant.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-metadata-required-metadata-for-validation.constant.ts @@ -11,7 +11,7 @@ type MetadataRequiredForValidation = { }; }; -// TODO deprecate in favor of ALL_METADATA_RELATIONS +// TODO deprecate in favor of ALL_METADATA_SERIALIZED_RELATION export const ALL_METADATA_REQUIRED_METADATA_FOR_VALIDATION = { fieldMetadata: { objectMetadata: true, diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-metadata-serialized-relation.constant.ts b/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-metadata-serialized-relation.constant.ts new file mode 100644 index 00000000000..8a9a403ee43 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-metadata-serialized-relation.constant.ts @@ -0,0 +1,53 @@ +import { type AllMetadataName } from 'twenty-shared/metadata'; +import { type Expect } from 'twenty-shared/testing'; + +import { type AllJsonbPropertiesWithSerializedPropertiesForMetadataName } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/constants/all-jsonb-properties-with-serialized-relation-by-metadata-name.constant'; + +type MetadataSerializedRelationProperties = { + [TSourceMetadataName in AllMetadataName]: [ + AllJsonbPropertiesWithSerializedPropertiesForMetadataName, + ] extends [never] + ? // eslint-disable-next-line @typescript-eslint/no-empty-object-type + {} + : Partial>; +}; + +export const ALL_METADATA_SERIALIZED_RELATION = { + agent: {}, + skill: {}, + commandMenuItem: {}, + navigationMenuItem: {}, + fieldMetadata: { + fieldMetadata: true, + }, + objectMetadata: {}, + view: {}, + viewField: {}, + viewFieldGroup: {}, + viewFilter: {}, + viewGroup: {}, + index: {}, + logicFunction: {}, + role: {}, + roleTarget: {}, + pageLayout: {}, + pageLayoutTab: {}, + pageLayoutWidget: { + fieldMetadata: true, + }, + rowLevelPermissionPredicate: {}, + rowLevelPermissionPredicateGroup: {}, + viewFilterGroup: {}, + frontComponent: {}, + webhook: {}, +} as const satisfies MetadataSerializedRelationProperties; + +// satisfies with complex mapped types involving nested generics doesn't always catch missing required keys +// eslint-disable-next-line unused-imports/no-unused-vars +type Assertions = [ + Expect< + AllMetadataName extends keyof typeof ALL_METADATA_SERIALIZED_RELATION + ? true + : false + >, +]; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-one-to-many-metadata-relations.constant.ts b/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-one-to-many-metadata-relations.constant.ts new file mode 100644 index 00000000000..13cf63ef707 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-one-to-many-metadata-relations.constant.ts @@ -0,0 +1,215 @@ +import { type AllMetadataName } from 'twenty-shared/metadata'; +import { type Expect } from 'twenty-shared/testing'; +import { type RemoveSuffix } from 'twenty-shared/types'; + +import { type ExtractEntityOneToManyEntityRelationProperties } from 'src/engine/metadata-modules/flat-entity/types/extract-entity-one-to-many-entity-relation-properties.type'; +import { type FromMetadataEntityToMetadataName } from 'src/engine/metadata-modules/flat-entity/types/from-metadata-entity-to-metadata-name.type'; +import { type MetadataEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-entity.type'; +import { type SyncableEntity } from 'src/engine/workspace-manager/types/syncable-entity.interface'; + +type OneToManyRelationValue< + TSourceMetadataName extends AllMetadataName, + TRelationProperty extends ExtractEntityOneToManyEntityRelationProperties< + MetadataEntity + >, +> = MetadataEntity[TRelationProperty] extends (infer TTargetEntity extends + SyncableEntity)[] + ? TRelationProperty extends string + ? { + metadataName: FromMetadataEntityToMetadataName; + flatEntityForeignKeyAggregator: `${RemoveSuffix}Ids`; + universalFlatEntityForeignKeyAggregator: `${RemoveSuffix}UniversalIdentifiers`; + } + : never + : null; + +type OneToManyMetadataRelationsProperties = { + [TSourceMetadataName in AllMetadataName]: { + [TRelationProperty in ExtractEntityOneToManyEntityRelationProperties< + MetadataEntity + >]: OneToManyRelationValue; + }; +}; + +export const ALL_ONE_TO_MANY_METADATA_RELATIONS = { + agent: {}, + skill: {}, + commandMenuItem: {}, + navigationMenuItem: {}, + fieldMetadata: { + fieldPermissions: null, + indexFieldMetadatas: null, + viewFields: { + metadataName: 'viewField', + flatEntityForeignKeyAggregator: 'viewFieldIds', + universalFlatEntityForeignKeyAggregator: 'viewFieldUniversalIdentifiers', + }, + viewFilters: { + metadataName: 'viewFilter', + flatEntityForeignKeyAggregator: 'viewFilterIds', + universalFlatEntityForeignKeyAggregator: 'viewFilterUniversalIdentifiers', + }, + kanbanAggregateOperationViews: { + metadataName: 'view', + flatEntityForeignKeyAggregator: 'kanbanAggregateOperationViewIds', + universalFlatEntityForeignKeyAggregator: + 'kanbanAggregateOperationViewUniversalIdentifiers', + }, + calendarViews: { + metadataName: 'view', + flatEntityForeignKeyAggregator: 'calendarViewIds', + universalFlatEntityForeignKeyAggregator: + 'calendarViewUniversalIdentifiers', + }, + mainGroupByFieldMetadataViews: { + metadataName: 'view', + flatEntityForeignKeyAggregator: 'mainGroupByFieldMetadataViewIds', + universalFlatEntityForeignKeyAggregator: + 'mainGroupByFieldMetadataViewUniversalIdentifiers', + }, + }, + objectMetadata: { + fields: { + metadataName: 'fieldMetadata', + flatEntityForeignKeyAggregator: 'fieldIds', + universalFlatEntityForeignKeyAggregator: 'fieldUniversalIdentifiers', + }, + indexMetadatas: { + metadataName: 'index', + flatEntityForeignKeyAggregator: 'indexMetadataIds', + universalFlatEntityForeignKeyAggregator: + 'indexMetadataUniversalIdentifiers', + }, + objectPermissions: null, + fieldPermissions: null, + views: { + metadataName: 'view', + flatEntityForeignKeyAggregator: 'viewIds', + universalFlatEntityForeignKeyAggregator: 'viewUniversalIdentifiers', + }, + }, + view: { + viewFields: { + metadataName: 'viewField', + flatEntityForeignKeyAggregator: 'viewFieldIds', + universalFlatEntityForeignKeyAggregator: 'viewFieldUniversalIdentifiers', + }, + viewFilters: { + metadataName: 'viewFilter', + flatEntityForeignKeyAggregator: 'viewFilterIds', + universalFlatEntityForeignKeyAggregator: 'viewFilterUniversalIdentifiers', + }, + viewFilterGroups: { + metadataName: 'viewFilterGroup', + flatEntityForeignKeyAggregator: 'viewFilterGroupIds', + universalFlatEntityForeignKeyAggregator: + 'viewFilterGroupUniversalIdentifiers', + }, + viewGroups: { + metadataName: 'viewGroup', + flatEntityForeignKeyAggregator: 'viewGroupIds', + universalFlatEntityForeignKeyAggregator: 'viewGroupUniversalIdentifiers', + }, + viewFieldGroups: { + metadataName: 'viewFieldGroup', + flatEntityForeignKeyAggregator: 'viewFieldGroupIds', + universalFlatEntityForeignKeyAggregator: + 'viewFieldGroupUniversalIdentifiers', + }, + // @ts-expect-error TODO migrate viewSort to v2 + viewSorts: null, + }, + viewField: {}, + viewFieldGroup: { + viewFields: { + metadataName: 'viewField', + flatEntityForeignKeyAggregator: 'viewFieldIds', + universalFlatEntityForeignKeyAggregator: 'viewFieldUniversalIdentifiers', + }, + }, + viewFilter: {}, + viewGroup: {}, + index: { + indexFieldMetadatas: null, + }, + logicFunction: {}, + role: { + roleTargets: { + metadataName: 'roleTarget', + flatEntityForeignKeyAggregator: 'roleTargetIds', + universalFlatEntityForeignKeyAggregator: 'roleTargetUniversalIdentifiers', + }, + objectPermissions: null, + permissionFlags: null, + fieldPermissions: null, + rowLevelPermissionPredicates: { + metadataName: 'rowLevelPermissionPredicate', + flatEntityForeignKeyAggregator: 'rowLevelPermissionPredicateIds', + universalFlatEntityForeignKeyAggregator: + 'rowLevelPermissionPredicateUniversalIdentifiers', + }, + rowLevelPermissionPredicateGroups: { + metadataName: 'rowLevelPermissionPredicateGroup', + flatEntityForeignKeyAggregator: 'rowLevelPermissionPredicateGroupIds', + universalFlatEntityForeignKeyAggregator: + 'rowLevelPermissionPredicateGroupUniversalIdentifiers', + }, + }, + roleTarget: {}, + pageLayout: { + tabs: { + metadataName: 'pageLayoutTab', + flatEntityForeignKeyAggregator: 'tabIds', + universalFlatEntityForeignKeyAggregator: 'tabUniversalIdentifiers', + }, + }, + pageLayoutTab: { + widgets: { + metadataName: 'pageLayoutWidget', + flatEntityForeignKeyAggregator: 'widgetIds', + universalFlatEntityForeignKeyAggregator: 'widgetUniversalIdentifiers', + }, + }, + pageLayoutWidget: {}, + rowLevelPermissionPredicate: {}, + rowLevelPermissionPredicateGroup: { + childRowLevelPermissionPredicateGroups: { + metadataName: 'rowLevelPermissionPredicateGroup', + flatEntityForeignKeyAggregator: + 'childRowLevelPermissionPredicateGroupIds', + universalFlatEntityForeignKeyAggregator: + 'childRowLevelPermissionPredicateGroupUniversalIdentifiers', + }, + rowLevelPermissionPredicates: { + metadataName: 'rowLevelPermissionPredicate', + flatEntityForeignKeyAggregator: 'rowLevelPermissionPredicateIds', + universalFlatEntityForeignKeyAggregator: + 'rowLevelPermissionPredicateUniversalIdentifiers', + }, + }, + viewFilterGroup: { + childViewFilterGroups: { + metadataName: 'viewFilterGroup', + flatEntityForeignKeyAggregator: 'childViewFilterGroupIds', + universalFlatEntityForeignKeyAggregator: + 'childViewFilterGroupUniversalIdentifiers', + }, + viewFilters: { + metadataName: 'viewFilter', + flatEntityForeignKeyAggregator: 'viewFilterIds', + universalFlatEntityForeignKeyAggregator: 'viewFilterUniversalIdentifiers', + }, + }, + frontComponent: {}, + webhook: {}, +} as const satisfies OneToManyMetadataRelationsProperties; + +// satisfies with complex mapped types involving nested generics doesn't always catch missing required keys +// eslint-disable-next-line unused-imports/no-unused-vars +type Assertions = [ + Expect< + AllMetadataName extends keyof typeof ALL_ONE_TO_MANY_METADATA_RELATIONS + ? true + : false + >, +]; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-entity/types/__tests__/metadata-many-to-one-join-column.type-test.ts b/packages/twenty-server/src/engine/metadata-modules/flat-entity/types/__tests__/metadata-many-to-one-join-column.type-test.ts index 214955e24c7..e4a09ecd95e 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-entity/types/__tests__/metadata-many-to-one-join-column.type-test.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-entity/types/__tests__/metadata-many-to-one-join-column.type-test.ts @@ -6,7 +6,6 @@ type FieldMetadataJoinColumns = MetadataManyToOneJoinColumn<'fieldMetadata'>; // eslint-disable-next-line unused-imports/no-unused-vars type Assertions = [ - // fieldMetadata foreign keys from ALL_METADATA_RELATIONS Expect< Equal< FieldMetadataJoinColumns, diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-entity/types/metadata-many-to-one-join-column.type.ts b/packages/twenty-server/src/engine/metadata-modules/flat-entity/types/metadata-many-to-one-join-column.type.ts index 3cd2f0b9246..3b256b6a66d 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-entity/types/metadata-many-to-one-join-column.type.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-entity/types/metadata-many-to-one-join-column.type.ts @@ -1,10 +1,10 @@ import { type AllMetadataName } from 'twenty-shared/metadata'; -import { type ALL_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-metadata-relations.constant'; +import { type ALL_MANY_TO_ONE_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-many-to-one-metadata-relations.constant'; type ExtractForeignKeys = { [K in keyof T]: T[K] extends { foreignKey: infer FK } ? FK : never; }[keyof T]; export type MetadataManyToOneJoinColumn = - ExtractForeignKeys<(typeof ALL_METADATA_RELATIONS)[T]['manyToOne']>; + ExtractForeignKeys<(typeof ALL_MANY_TO_ONE_METADATA_RELATIONS)[T]>; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-entity/types/metadata-many-to-one-related-metadata-names.type.ts b/packages/twenty-server/src/engine/metadata-modules/flat-entity/types/metadata-many-to-one-related-metadata-names.type.ts index 94abeb08cf7..8afb6267ea7 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-entity/types/metadata-many-to-one-related-metadata-names.type.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-entity/types/metadata-many-to-one-related-metadata-names.type.ts @@ -1,6 +1,6 @@ import { type AllMetadataName } from 'twenty-shared/metadata'; -import { type ALL_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-metadata-relations.constant'; +import { type ALL_MANY_TO_ONE_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-many-to-one-metadata-relations.constant'; type ExtractMetadataNames = { [K in keyof T]: T[K] extends { metadataName: infer M } ? M : never; @@ -8,6 +8,6 @@ type ExtractMetadataNames = { export type MetadataManyToOneRelatedMetadataNames = Extract< - ExtractMetadataNames<(typeof ALL_METADATA_RELATIONS)[T]['manyToOne']>, + ExtractMetadataNames<(typeof ALL_MANY_TO_ONE_METADATA_RELATIONS)[T]>, AllMetadataName >; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-entity/types/metadata-one-to-many-related-metadata-names.type.ts b/packages/twenty-server/src/engine/metadata-modules/flat-entity/types/metadata-one-to-many-related-metadata-names.type.ts index 13b295d4989..b0412fefd43 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-entity/types/metadata-one-to-many-related-metadata-names.type.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-entity/types/metadata-one-to-many-related-metadata-names.type.ts @@ -1,6 +1,6 @@ import { type AllMetadataName } from 'twenty-shared/metadata'; -import { type ALL_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-metadata-relations.constant'; +import { type ALL_ONE_TO_MANY_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-one-to-many-metadata-relations.constant'; type ExtractMetadataNames = { [K in keyof T]: T[K] extends { metadataName: infer M } ? M : never; @@ -8,6 +8,6 @@ type ExtractMetadataNames = { export type MetadataOneToManyRelatedMetadataNames = Extract< - ExtractMetadataNames<(typeof ALL_METADATA_RELATIONS)[T]['oneToMany']>, + ExtractMetadataNames<(typeof ALL_ONE_TO_MANY_METADATA_RELATIONS)[T]>, AllMetadataName >; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-entity/utils/__tests__/__snapshots__/sort-metadata-names-children-first.util.spec.ts.snap b/packages/twenty-server/src/engine/metadata-modules/flat-entity/utils/__tests__/__snapshots__/sort-metadata-names-children-first.util.spec.ts.snap index ba7d88b8a9c..065ccb24653 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-entity/utils/__tests__/__snapshots__/sort-metadata-names-children-first.util.spec.ts.snap +++ b/packages/twenty-server/src/engine/metadata-modules/flat-entity/utils/__tests__/__snapshots__/sort-metadata-names-children-first.util.spec.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +// Jest Snapshot v1, https://goo.gl/fbAQLP exports[`sortMetadataNamesChildrenFirst should return metadata names sorted with children first (most manyToOne relations first) 1`] = ` [ @@ -23,7 +23,7 @@ exports[`sortMetadataNamesChildrenFirst should return metadata names sorted with "webhook", "view", "fieldMetadata", - "role", "objectMetadata", + "role", ] `; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-entity/utils/add-flat-entity-to-flat-entity-and-related-entity-maps-through-mutation-or-throw.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-entity/utils/add-flat-entity-to-flat-entity-and-related-entity-maps-through-mutation-or-throw.util.ts index 706f1f5d3b3..9475eb66591 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-entity/utils/add-flat-entity-to-flat-entity-and-related-entity-maps-through-mutation-or-throw.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-entity/utils/add-flat-entity-to-flat-entity-and-related-entity-maps-through-mutation-or-throw.util.ts @@ -1,7 +1,8 @@ import { type AllMetadataName } from 'twenty-shared/metadata'; import { isDefined } from 'twenty-shared/utils'; -import { ALL_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-metadata-relations.constant'; +import { ALL_MANY_TO_ONE_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-many-to-one-metadata-relations.constant'; +import { ALL_ONE_TO_MANY_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-one-to-many-metadata-relations.constant'; import { FlatEntityMapsException, FlatEntityMapsExceptionCode, @@ -45,29 +46,46 @@ export const addFlatEntityToFlatEntityAndRelatedEntityMapsThroughMutationOrThrow selfFlatEntityMaps.universalIdentifierById[flatEntity.id] = flatEntity.universalIdentifier; - const idBasedManyToOneRelations = Object.values( - ALL_METADATA_RELATIONS[metadataName].manyToOne, - ) as Array<{ - metadataName: AllMetadataName; - flatEntityForeignKeyAggregator: keyof MetadataFlatEntity; - foreignKey: keyof MetadataFlatEntity; - } | null>; + const manyToOneRelations = ALL_MANY_TO_ONE_METADATA_RELATIONS[metadataName]; - for (const idBasedRelation of idBasedManyToOneRelations) { - if (!isDefined(idBasedRelation)) { + for (const relationPropertyName of Object.keys(manyToOneRelations)) { + const relation = manyToOneRelations[ + relationPropertyName as keyof typeof manyToOneRelations + ] as { + metadataName: AllMetadataName; + foreignKey: string; + inverseOneToManyProperty: string | null; + } | null; + + if (!isDefined(relation)) { continue; } const { metadataName: relatedMetadataName, - flatEntityForeignKeyAggregator, foreignKey, - } = idBasedRelation; + inverseOneToManyProperty, + } = relation; - if (!isDefined(flatEntityForeignKeyAggregator)) { + if (!isDefined(inverseOneToManyProperty)) { continue; } + const oneToManyRelations = + ALL_ONE_TO_MANY_METADATA_RELATIONS[relatedMetadataName]; + + const inverseRelation = oneToManyRelations[ + inverseOneToManyProperty as keyof typeof oneToManyRelations + ] as { + flatEntityForeignKeyAggregator: string; + } | null; + + if (!isDefined(inverseRelation)) { + continue; + } + + const { flatEntityForeignKeyAggregator } = inverseRelation; + const relatedFlatEntityMapsKey = getMetadataFlatEntityMapsKey(relatedMetadataName); @@ -75,9 +93,9 @@ export const addFlatEntityToFlatEntityAndRelatedEntityMapsThroughMutationOrThrow relatedFlatEntityMapsKey as MetadataRelatedFlatEntityMapsKeys ] as FlatEntityMaps>; - const flatEntityRelatedEntityForeignKeyValue = flatEntity[foreignKey] as - | string - | undefined; + const flatEntityRelatedEntityForeignKeyValue = ( + flatEntity as unknown as Record + )[foreignKey]; if (!isDefined(flatEntityRelatedEntityForeignKeyValue)) { continue; @@ -103,9 +121,9 @@ export const addFlatEntityToFlatEntityAndRelatedEntityMapsThroughMutationOrThrow const updatedRelatedEntity = { ...relatedFlatEntity, [flatEntityForeignKeyAggregator]: [ - ...(relatedFlatEntity[ + ...((relatedFlatEntity as unknown as Record)[ flatEntityForeignKeyAggregator - ] as unknown as string[]), + ] ?? []), flatEntity.id, ], }; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-entity/utils/delete-flat-entity-from-flat-entity-and-related-entity-maps-through-mutation-or-throw.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-entity/utils/delete-flat-entity-from-flat-entity-and-related-entity-maps-through-mutation-or-throw.util.ts index 7b50f591095..d2ee3e3df00 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-entity/utils/delete-flat-entity-from-flat-entity-and-related-entity-maps-through-mutation-or-throw.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-entity/utils/delete-flat-entity-from-flat-entity-and-related-entity-maps-through-mutation-or-throw.util.ts @@ -1,7 +1,8 @@ import { type AllMetadataName } from 'twenty-shared/metadata'; import { isDefined } from 'twenty-shared/utils'; -import { ALL_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-metadata-relations.constant'; +import { ALL_MANY_TO_ONE_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-many-to-one-metadata-relations.constant'; +import { ALL_ONE_TO_MANY_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-one-to-many-metadata-relations.constant'; import { FlatEntityMapsException, FlatEntityMapsExceptionCode, @@ -38,29 +39,46 @@ export const deleteFlatEntityFromFlatEntityAndRelatedEntityMapsThroughMutationOr }, ); - const idBasedManyToOneRelations = Object.values( - ALL_METADATA_RELATIONS[metadataName].manyToOne, - ) as Array<{ - metadataName: AllMetadataName; - flatEntityForeignKeyAggregator: keyof MetadataFlatEntity; - foreignKey: keyof MetadataFlatEntity; - } | null>; + const manyToOneRelations = ALL_MANY_TO_ONE_METADATA_RELATIONS[metadataName]; - for (const idBasedRelation of idBasedManyToOneRelations) { - if (!isDefined(idBasedRelation)) { + for (const relationPropertyName of Object.keys(manyToOneRelations)) { + const relation = manyToOneRelations[ + relationPropertyName as keyof typeof manyToOneRelations + ] as { + metadataName: AllMetadataName; + foreignKey: string; + inverseOneToManyProperty: string | null; + } | null; + + if (!isDefined(relation)) { continue; } const { metadataName: relatedMetadataName, - flatEntityForeignKeyAggregator, foreignKey, - } = idBasedRelation; + inverseOneToManyProperty, + } = relation; - if (!isDefined(flatEntityForeignKeyAggregator)) { + if (!isDefined(inverseOneToManyProperty)) { continue; } + const oneToManyRelations = + ALL_ONE_TO_MANY_METADATA_RELATIONS[relatedMetadataName]; + + const inverseRelation = oneToManyRelations[ + inverseOneToManyProperty as keyof typeof oneToManyRelations + ] as { + flatEntityForeignKeyAggregator: string; + } | null; + + if (!isDefined(inverseRelation)) { + continue; + } + + const { flatEntityForeignKeyAggregator } = inverseRelation; + const relatedFlatEntityMapsKey = getMetadataFlatEntityMapsKey(relatedMetadataName); @@ -68,9 +86,9 @@ export const deleteFlatEntityFromFlatEntityAndRelatedEntityMapsThroughMutationOr relatedFlatEntityMapsKey as MetadataRelatedFlatEntityMapsKeys ] as FlatEntityMaps>; - const flatEntityRelatedEntityForeignKeyValue = flatEntity[foreignKey] as - | string - | undefined; + const flatEntityRelatedEntityForeignKeyValue = ( + flatEntity as unknown as Record + )[foreignKey]; if (!isDefined(flatEntityRelatedEntityForeignKeyValue)) { continue; @@ -100,9 +118,9 @@ export const deleteFlatEntityFromFlatEntityAndRelatedEntityMapsThroughMutationOr const updatedRelatedEntity = { ...relatedFlatEntity, [flatEntityForeignKeyAggregator]: ( - relatedFlatEntity[ + (relatedFlatEntity as unknown as Record)[ flatEntityForeignKeyAggregator - ] as unknown as string[] + ] ?? [] ).filter((id) => id !== flatEntity.id), }; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-entity/utils/get-metadata-entity-relation-properties.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-entity/utils/get-metadata-entity-relation-properties.util.ts index 34e83528c6d..a48e16c6744 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-entity/utils/get-metadata-entity-relation-properties.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-entity/utils/get-metadata-entity-relation-properties.util.ts @@ -1,17 +1,16 @@ import { type AllMetadataName } from 'twenty-shared/metadata'; -import { ALL_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-metadata-relations.constant'; +import { ALL_MANY_TO_ONE_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-many-to-one-metadata-relations.constant'; +import { ALL_ONE_TO_MANY_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-one-to-many-metadata-relations.constant'; export const getMetadataEntityRelationProperties = ( metadataName: T, ) => { - const relationProperties = ALL_METADATA_RELATIONS[metadataName]; - return [ - ...Object.keys(relationProperties.manyToOne), - ...Object.keys(relationProperties.oneToMany), + ...Object.keys(ALL_MANY_TO_ONE_METADATA_RELATIONS[metadataName]), + ...Object.keys(ALL_ONE_TO_MANY_METADATA_RELATIONS[metadataName]), ] as ( - | keyof (typeof ALL_METADATA_RELATIONS)[T]['manyToOne'] - | keyof (typeof ALL_METADATA_RELATIONS)[T]['oneToMany'] + | keyof (typeof ALL_MANY_TO_ONE_METADATA_RELATIONS)[T] + | keyof (typeof ALL_ONE_TO_MANY_METADATA_RELATIONS)[T] )[]; }; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-entity/utils/get-metadata-many-to-one-related-names.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-entity/utils/get-metadata-many-to-one-related-names.util.ts index 0e5b5aade3a..92df5876697 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-entity/utils/get-metadata-many-to-one-related-names.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-entity/utils/get-metadata-many-to-one-related-names.util.ts @@ -1,14 +1,12 @@ import { type AllMetadataName } from 'twenty-shared/metadata'; -import { ALL_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-metadata-relations.constant'; +import { ALL_MANY_TO_ONE_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-many-to-one-metadata-relations.constant'; import { type MetadataManyToOneRelatedMetadataNames } from 'src/engine/metadata-modules/flat-entity/types/metadata-many-to-one-related-metadata-names.type'; export const getMetadataManyToOneRelatedNames = ( metadataName: T, ): MetadataManyToOneRelatedMetadataNames[] => { - const relations = ALL_METADATA_RELATIONS[metadataName]; - - return Object.values(relations.manyToOne) + return Object.values(ALL_MANY_TO_ONE_METADATA_RELATIONS[metadataName]) .filter((relation) => relation !== null) .map( (relation) => relation.metadataName, diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-entity/utils/get-metadata-one-to-many-related-names.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-entity/utils/get-metadata-one-to-many-related-names.util.ts index 0369e7aa801..61eee25e0d5 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-entity/utils/get-metadata-one-to-many-related-names.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-entity/utils/get-metadata-one-to-many-related-names.util.ts @@ -1,14 +1,12 @@ import { type AllMetadataName } from 'twenty-shared/metadata'; -import { ALL_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-metadata-relations.constant'; +import { ALL_ONE_TO_MANY_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-one-to-many-metadata-relations.constant'; import { type MetadataOneToManyRelatedMetadataNames } from 'src/engine/metadata-modules/flat-entity/types/metadata-one-to-many-related-metadata-names.type'; export const getMetadataOneToManyRelatedNames = ( metadataName: T, ): MetadataOneToManyRelatedMetadataNames[] => { - const relations = ALL_METADATA_RELATIONS[metadataName]; - - return Object.values(relations.oneToMany) + return Object.values(ALL_ONE_TO_MANY_METADATA_RELATIONS[metadataName]) .filter((relation) => relation !== null) .map( (relation) => relation.metadataName, diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-entity/utils/get-metadata-serialized-relation-names.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-entity/utils/get-metadata-serialized-relation-names.util.ts index db1f6222fc3..42e716ce4f4 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-entity/utils/get-metadata-serialized-relation-names.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-entity/utils/get-metadata-serialized-relation-names.util.ts @@ -1,15 +1,11 @@ import { type AllMetadataName } from 'twenty-shared/metadata'; -import { ALL_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-metadata-relations.constant'; +import { ALL_METADATA_SERIALIZED_RELATION } from 'src/engine/metadata-modules/flat-entity/constant/all-metadata-serialized-relation.constant'; export const getMetadataSerializedRelationNames = ( metadataName: AllMetadataName, ): AllMetadataName[] => { - const relations = ALL_METADATA_RELATIONS[metadataName]; - - if (!('serializedRelations' in relations)) { - return []; - } - - return Object.keys(relations.serializedRelations) as AllMetadataName[]; + return Object.keys( + ALL_METADATA_SERIALIZED_RELATION[metadataName], + ) as AllMetadataName[]; }; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-entity/utils/resolve-entity-relation-universal-identifiers.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-entity/utils/resolve-entity-relation-universal-identifiers.util.ts index 3dcaf7eaf29..44e657efce3 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-entity/utils/resolve-entity-relation-universal-identifiers.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-entity/utils/resolve-entity-relation-universal-identifiers.util.ts @@ -1,25 +1,19 @@ import { t } from '@lingui/core/macro'; import { type AllMetadataName } from 'twenty-shared/metadata'; import { isDefined } from 'twenty-shared/utils'; -import { type RemoveSuffix } from 'twenty-shared/types'; -import { - ALL_METADATA_RELATIONS, - type MetadataManyToOneRelationConfiguration, -} from 'src/engine/metadata-modules/flat-entity/constant/all-metadata-relations.constant'; +import { ALL_MANY_TO_ONE_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-many-to-one-metadata-relations.constant'; import { FlatEntityMapsException, FlatEntityMapsExceptionCode, } from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception'; import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type'; -import { type ExtractEntityManyToOneEntityRelationProperties } from 'src/engine/metadata-modules/flat-entity/types/extract-entity-many-to-one-entity-relation-properties.type'; -import { type MetadataEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-entity.type'; import { type MetadataManyToOneJoinColumn } from 'src/engine/metadata-modules/flat-entity/types/metadata-many-to-one-join-column.type'; import { type MetadataToFlatEntityMapsKey } from 'src/engine/metadata-modules/flat-entity/types/metadata-to-flat-entity-maps-key'; import { getMetadataFlatEntityMapsKey } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-flat-entity-maps-key.util'; type ManyToOneConfig = - (typeof ALL_METADATA_RELATIONS)[T]['manyToOne']; + (typeof ALL_MANY_TO_ONE_METADATA_RELATIONS)[T]; type TargetMetadataNamesForForeignKeys< T extends AllMetadataName, @@ -49,9 +43,10 @@ type ResolvedUniversalIdentifiers< > = { [K in keyof ManyToOneConfig as ManyToOneConfig[K] extends { foreignKey: infer FK extends string; + universalForeignKey: infer UFK extends string; } ? FK extends TProvidedKeys - ? `${RemoveSuffix}UniversalIdentifier` + ? UFK : never : never]: ManyToOneConfig[K] extends { isNullable: true } ? string | null @@ -73,15 +68,19 @@ export const resolveEntityRelationUniversalIdentifiers = < foreignKeyValues: Record; flatEntityMaps: RequiredFlatEntityMapsForForeignKeys; }): ResolvedUniversalIdentifiers => { - const relations = ALL_METADATA_RELATIONS[metadataName].manyToOne; + const relationEntries = ALL_MANY_TO_ONE_METADATA_RELATIONS[metadataName]; const result: Record = {}; - for (const relation of Object.values( - relations, - ) as MetadataManyToOneRelationConfiguration< - T, - ExtractEntityManyToOneEntityRelationProperties> - >[]) { + for (const relationPropertyName of Object.keys(relationEntries)) { + const relation = relationEntries[ + relationPropertyName as keyof typeof relationEntries + ] as { + foreignKey: string; + metadataName: AllMetadataName; + isNullable: boolean; + universalForeignKey: string; + } | null; + if (!isDefined(relation)) { continue; } @@ -90,6 +89,7 @@ export const resolveEntityRelationUniversalIdentifiers = < foreignKey, metadataName: targetMetadataName, isNullable, + universalForeignKey, } = relation; if (!Object.prototype.hasOwnProperty.call(foreignKeyValues, foreignKey)) { @@ -104,14 +104,8 @@ export const resolveEntityRelationUniversalIdentifiers = < ) as keyof RequiredFlatEntityMapsForForeignKeys; const targetFlatEntityMaps = flatEntityMaps[mapsKey]; - // TODO refactor using the new ALL_METADATA_UNIVERSAL_RELATION afterwards - const universalIdentifierKey = foreignKey.replace( - /Id$/, - 'UniversalIdentifier', - ); - if (isNullable && !isDefined(foreignKeyValue)) { - result[universalIdentifierKey] = null; + result[universalForeignKey] = null; continue; } @@ -126,7 +120,7 @@ export const resolveEntityRelationUniversalIdentifiers = < ); } - result[universalIdentifierKey] = resolvedUniversalIdentifier; + result[universalForeignKey] = resolvedUniversalIdentifier; } return result as ResolvedUniversalIdentifiers; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type.ts b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type.ts index 575d3b59ac0..27db7ad710b 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type.ts @@ -2,7 +2,7 @@ import { type FlatEntityFrom } from 'src/engine/metadata-modules/flat-entity/typ import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; type BaseFlatObjectMetadata = FlatEntityFrom< - Omit + Omit >; export type FlatObjectMetadata = BaseFlatObjectMetadata & { // NOTE: below fields are not reflected on the final UniversalFlatEntity either they should we should define a common source diff --git a/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.entity.ts b/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.entity.ts index 00a4e3f0db2..2393dd82a4b 100644 --- a/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.entity.ts +++ b/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.entity.ts @@ -117,12 +117,6 @@ export class ObjectMetadataEntity }) indexMetadatas: Relation; - @OneToMany( - () => FieldMetadataEntity, - (field) => field.relationTargetObjectMetadataId, - ) - targetRelationFields: Relation; - @ManyToOne(() => DataSourceEntity, (dataSource) => dataSource.objects, { onDelete: 'CASCADE', }) diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/constants/__tests__/__snapshots__/all-universal-flat-entity-foreign-key-aggregator-properties.constant.spec.ts.snap b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/constants/__tests__/__snapshots__/all-universal-flat-entity-foreign-key-aggregator-properties.constant.spec.ts.snap index a3c7a792ddc..d1aa610ce95 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/constants/__tests__/__snapshots__/all-universal-flat-entity-foreign-key-aggregator-properties.constant.spec.ts.snap +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/constants/__tests__/__snapshots__/all-universal-flat-entity-foreign-key-aggregator-properties.constant.spec.ts.snap @@ -1,15 +1,15 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +// Jest Snapshot v1, https://goo.gl/fbAQLP exports[`ALL_UNIVERSAL_FLAT_ENTITY_FOREIGN_KEY_AGGREGATOR_PROPERTIES should match snapshot 1`] = ` { "agent": [], "commandMenuItem": [], "fieldMetadata": [ - "calendarViewUniversalIdentifiers", - "kanbanAggregateOperationViewUniversalIdentifiers", - "mainGroupByFieldMetadataViewUniversalIdentifiers", "viewFieldUniversalIdentifiers", "viewFilterUniversalIdentifiers", + "kanbanAggregateOperationViewUniversalIdentifiers", + "calendarViewUniversalIdentifiers", + "mainGroupByFieldMetadataViewUniversalIdentifiers", ], "frontComponent": [], "index": [], @@ -17,8 +17,8 @@ exports[`ALL_UNIVERSAL_FLAT_ENTITY_FOREIGN_KEY_AGGREGATOR_PROPERTIES should matc "navigationMenuItem": [], "objectMetadata": [ "fieldUniversalIdentifiers", - "viewUniversalIdentifiers", "indexMetadataUniversalIdentifiers", + "viewUniversalIdentifiers", ], "pageLayout": [ "tabUniversalIdentifiers", @@ -29,19 +29,22 @@ exports[`ALL_UNIVERSAL_FLAT_ENTITY_FOREIGN_KEY_AGGREGATOR_PROPERTIES should matc "pageLayoutWidget": [], "role": [ "roleTargetUniversalIdentifiers", + "rowLevelPermissionPredicateUniversalIdentifiers", + "rowLevelPermissionPredicateGroupUniversalIdentifiers", ], "roleTarget": [], "rowLevelPermissionPredicate": [], "rowLevelPermissionPredicateGroup": [ "childRowLevelPermissionPredicateGroupUniversalIdentifiers", + "rowLevelPermissionPredicateUniversalIdentifiers", ], "skill": [], "view": [ "viewFieldUniversalIdentifiers", - "viewFieldGroupUniversalIdentifiers", "viewFilterUniversalIdentifiers", - "viewGroupUniversalIdentifiers", "viewFilterGroupUniversalIdentifiers", + "viewGroupUniversalIdentifiers", + "viewFieldGroupUniversalIdentifiers", ], "viewField": [], "viewFieldGroup": [ @@ -49,8 +52,8 @@ exports[`ALL_UNIVERSAL_FLAT_ENTITY_FOREIGN_KEY_AGGREGATOR_PROPERTIES should matc ], "viewFilter": [], "viewFilterGroup": [ - "viewFilterUniversalIdentifiers", "childViewFilterGroupUniversalIdentifiers", + "viewFilterUniversalIdentifiers", ], "viewGroup": [], "webhook": [], diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/constants/all-universal-flat-entity-foreign-key-aggregator-properties.constant.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/constants/all-universal-flat-entity-foreign-key-aggregator-properties.constant.ts index 0bd9ccce7b9..419e89c948d 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/constants/all-universal-flat-entity-foreign-key-aggregator-properties.constant.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/constants/all-universal-flat-entity-foreign-key-aggregator-properties.constant.ts @@ -4,30 +4,21 @@ import { } from 'twenty-shared/metadata'; import { isDefined } from 'twenty-shared/utils'; -import { ALL_UNIVERSAL_METADATA_RELATIONS } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/constants/all-universal-metadata-relations.constant'; +import { ALL_ONE_TO_MANY_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-one-to-many-metadata-relations.constant'; -type ExtractForeignKeyAggregatorFromManyToOneRelations< - ManyToOneRelations, - TargetMetadataName extends AllMetadataName, -> = { - [K in keyof ManyToOneRelations]: ManyToOneRelations[K] extends { - metadataName: TargetMetadataName; - universalFlatEntityForeignKeyAggregator: infer Agg; +type ExtractUniversalForeignKeyAggregators = { + [K in keyof OneToManyRelations]: OneToManyRelations[K] extends { + universalFlatEntityForeignKeyAggregator: infer Agg extends string; } - ? Agg extends string - ? Agg - : never + ? Agg : never; -}[keyof ManyToOneRelations]; +}[keyof OneToManyRelations]; export type ExtractUniversalForeignKeyAggregatorForMetadataName< T extends AllMetadataName, -> = { - [M in AllMetadataName]: ExtractForeignKeyAggregatorFromManyToOneRelations< - (typeof ALL_UNIVERSAL_METADATA_RELATIONS)[M]['manyToOne'], - T - >; -}[AllMetadataName]; +> = ExtractUniversalForeignKeyAggregators< + (typeof ALL_ONE_TO_MANY_METADATA_RELATIONS)[T] +>; type UniversalFlatEntityForeignKeyAggregatorProperties = { [P in AllMetadataName]: ExtractUniversalForeignKeyAggregatorForMetadataName

[]; @@ -36,25 +27,20 @@ type UniversalFlatEntityForeignKeyAggregatorProperties = { const computeForeignKeyAggregatorProperties = ( metadataName: T, ): ExtractUniversalForeignKeyAggregatorForMetadataName[] => { + const oneToManyRelations = ALL_ONE_TO_MANY_METADATA_RELATIONS[metadataName]; + const aggregatorProperties: ExtractUniversalForeignKeyAggregatorForMetadataName[] = []; - for (const relationsEntry of Object.values( - ALL_UNIVERSAL_METADATA_RELATIONS, - )) { - for (const relation of Object.values(relationsEntry.manyToOne)) { - if (!isDefined(relation)) { - continue; - } + for (const relation of Object.values(oneToManyRelations)) { + if (!isDefined(relation)) { + continue; + } - if ( - relation.metadataName === metadataName && - isDefined(relation.universalFlatEntityForeignKeyAggregator) - ) { - aggregatorProperties.push( - relation.universalFlatEntityForeignKeyAggregator as ExtractUniversalForeignKeyAggregatorForMetadataName, - ); - } + if (isDefined(relation.universalFlatEntityForeignKeyAggregator)) { + aggregatorProperties.push( + relation.universalFlatEntityForeignKeyAggregator as ExtractUniversalForeignKeyAggregatorForMetadataName, + ); } } diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/constants/all-universal-metadata-relations.constant.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/constants/all-universal-metadata-relations.constant.ts deleted file mode 100644 index ae6581c9fe7..00000000000 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/constants/all-universal-metadata-relations.constant.ts +++ /dev/null @@ -1,543 +0,0 @@ -import { type AllMetadataName } from 'twenty-shared/metadata'; - -import { type ALL_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-metadata-relations.constant'; - -export type ToUniversalForeignKey = - T extends `${infer Prefix}Id` ? `${Prefix}UniversalIdentifier` : never; - -type ToUniversalAggregator = T extends `${infer Prefix}Ids` - ? `${Prefix}UniversalIdentifiers` - : never; - -export type ToUniversalMetadataManyToOneRelationConfiguration = T extends { - metadataName: infer M extends AllMetadataName; - foreignKey: infer FK extends string; - flatEntityForeignKeyAggregator: infer Agg; - isNullable: infer N extends boolean; -} - ? { - metadataName: M; - foreignKey: FK; - universalForeignKey: ToUniversalForeignKey; - universalFlatEntityForeignKeyAggregator: Agg extends string - ? ToUniversalAggregator - : null; - isNullable: N; - } - : null; - -type ToUniversalManyToOneRelations = { - [K in keyof T]: ToUniversalMetadataManyToOneRelationConfiguration; -}; - -type ToUniversalOneToManyRelations = T; - -export type UniversalMetadataRelationsProperties = { - [M in AllMetadataName]: { - manyToOne: ToUniversalManyToOneRelations< - (typeof ALL_METADATA_RELATIONS)[M]['manyToOne'] - >; - oneToMany: ToUniversalOneToManyRelations< - (typeof ALL_METADATA_RELATIONS)[M]['oneToMany'] - >; - }; -}; - -export const ALL_UNIVERSAL_METADATA_RELATIONS = { - agent: { - manyToOne: { - workspace: null, - application: null, - }, - oneToMany: {}, - }, - skill: { - manyToOne: { - workspace: null, - application: null, - }, - oneToMany: {}, - }, - commandMenuItem: { - manyToOne: { - workspace: null, - application: null, - availabilityObjectMetadata: { - metadataName: 'objectMetadata', - foreignKey: 'availabilityObjectMetadataId', - universalFlatEntityForeignKeyAggregator: null, - universalForeignKey: 'availabilityObjectMetadataUniversalIdentifier', - isNullable: true, - }, - frontComponent: { - metadataName: 'frontComponent', - foreignKey: 'frontComponentId', - universalFlatEntityForeignKeyAggregator: null, - universalForeignKey: 'frontComponentUniversalIdentifier', - isNullable: true, - }, - }, - oneToMany: {}, - }, - navigationMenuItem: { - manyToOne: { - workspace: null, - userWorkspace: null, - application: null, - targetObjectMetadata: { - metadataName: 'objectMetadata', - foreignKey: 'targetObjectMetadataId', - universalFlatEntityForeignKeyAggregator: null, - universalForeignKey: 'targetObjectMetadataUniversalIdentifier', - isNullable: true, - }, - folder: { - metadataName: 'navigationMenuItem', - foreignKey: 'folderId', - universalFlatEntityForeignKeyAggregator: null, - universalForeignKey: 'folderUniversalIdentifier', - isNullable: true, - }, - view: { - metadataName: 'view', - foreignKey: 'viewId', - universalFlatEntityForeignKeyAggregator: null, - universalForeignKey: 'viewUniversalIdentifier', - isNullable: true, - }, - }, - oneToMany: {}, - }, - fieldMetadata: { - manyToOne: { - object: { - metadataName: 'objectMetadata', - foreignKey: 'objectMetadataId', - universalFlatEntityForeignKeyAggregator: 'fieldUniversalIdentifiers', - universalForeignKey: 'objectMetadataUniversalIdentifier', - isNullable: false, - }, - workspace: null, - application: null, - relationTargetFieldMetadata: { - metadataName: 'fieldMetadata', - foreignKey: 'relationTargetFieldMetadataId', - universalFlatEntityForeignKeyAggregator: null, - universalForeignKey: 'relationTargetFieldMetadataUniversalIdentifier', - isNullable: true, - }, - relationTargetObjectMetadata: { - metadataName: 'objectMetadata', - foreignKey: 'relationTargetObjectMetadataId', - universalFlatEntityForeignKeyAggregator: null, - universalForeignKey: 'relationTargetObjectMetadataUniversalIdentifier', - isNullable: true, - }, - }, - oneToMany: { - fieldPermissions: null, - indexFieldMetadatas: null, - viewFields: { metadataName: 'viewField' }, - viewFilters: { metadataName: 'viewFilter' }, - kanbanAggregateOperationViews: { metadataName: 'view' }, - calendarViews: { metadataName: 'view' }, - mainGroupByFieldMetadataViews: { metadataName: 'view' }, - }, - }, - objectMetadata: { - manyToOne: { - dataSource: null, - workspace: null, - application: null, - }, - oneToMany: { - fields: { metadataName: 'fieldMetadata' }, - indexMetadatas: { metadataName: 'index' }, - targetRelationFields: { metadataName: 'fieldMetadata' }, - objectPermissions: null, - fieldPermissions: null, - views: { metadataName: 'view' }, - }, - }, - view: { - manyToOne: { - objectMetadata: { - metadataName: 'objectMetadata', - foreignKey: 'objectMetadataId', - universalFlatEntityForeignKeyAggregator: 'viewUniversalIdentifiers', - universalForeignKey: 'objectMetadataUniversalIdentifier', - isNullable: false, - }, - workspace: null, - createdBy: null, - application: null, - calendarFieldMetadata: { - metadataName: 'fieldMetadata', - foreignKey: 'calendarFieldMetadataId', - universalFlatEntityForeignKeyAggregator: - 'calendarViewUniversalIdentifiers', - universalForeignKey: 'calendarFieldMetadataUniversalIdentifier', - isNullable: true, - }, - kanbanAggregateOperationFieldMetadata: { - metadataName: 'fieldMetadata', - foreignKey: 'kanbanAggregateOperationFieldMetadataId', - universalFlatEntityForeignKeyAggregator: - 'kanbanAggregateOperationViewUniversalIdentifiers', - universalForeignKey: - 'kanbanAggregateOperationFieldMetadataUniversalIdentifier', - isNullable: true, - }, - mainGroupByFieldMetadata: { - metadataName: 'fieldMetadata', - foreignKey: 'mainGroupByFieldMetadataId', - universalFlatEntityForeignKeyAggregator: - 'mainGroupByFieldMetadataViewUniversalIdentifiers', - universalForeignKey: 'mainGroupByFieldMetadataUniversalIdentifier', - isNullable: true, - }, - }, - oneToMany: { - viewFields: { metadataName: 'viewField' }, - viewFilters: { metadataName: 'viewFilter' }, - viewFilterGroups: { metadataName: 'viewFilterGroup' }, - viewGroups: { metadataName: 'viewGroup' }, - viewFieldGroups: { metadataName: 'viewFieldGroup' }, - // TODO migrate viewSort to v2 - viewSorts: null, - }, - }, - viewField: { - manyToOne: { - fieldMetadata: { - metadataName: 'fieldMetadata', - foreignKey: 'fieldMetadataId', - universalFlatEntityForeignKeyAggregator: - 'viewFieldUniversalIdentifiers', - universalForeignKey: 'fieldMetadataUniversalIdentifier', - isNullable: false, - }, - view: { - metadataName: 'view', - foreignKey: 'viewId', - universalFlatEntityForeignKeyAggregator: - 'viewFieldUniversalIdentifiers', - universalForeignKey: 'viewUniversalIdentifier', - isNullable: false, - }, - viewFieldGroup: { - metadataName: 'viewFieldGroup', - foreignKey: 'viewFieldGroupId', - universalFlatEntityForeignKeyAggregator: - 'viewFieldUniversalIdentifiers', - universalForeignKey: 'viewFieldGroupUniversalIdentifier', - isNullable: true, - }, - workspace: null, - application: null, - }, - oneToMany: {}, - }, - viewFieldGroup: { - manyToOne: { - view: { - metadataName: 'view', - foreignKey: 'viewId', - universalFlatEntityForeignKeyAggregator: - 'viewFieldGroupUniversalIdentifiers', - universalForeignKey: 'viewUniversalIdentifier', - isNullable: false, - }, - workspace: null, - application: null, - }, - oneToMany: { - viewFields: { metadataName: 'viewField' }, - }, - }, - viewFilter: { - manyToOne: { - fieldMetadata: { - metadataName: 'fieldMetadata', - foreignKey: 'fieldMetadataId', - universalFlatEntityForeignKeyAggregator: - 'viewFilterUniversalIdentifiers', - universalForeignKey: 'fieldMetadataUniversalIdentifier', - isNullable: false, - }, - view: { - metadataName: 'view', - foreignKey: 'viewId', - universalFlatEntityForeignKeyAggregator: - 'viewFilterUniversalIdentifiers', - universalForeignKey: 'viewUniversalIdentifier', - isNullable: false, - }, - viewFilterGroup: { - metadataName: 'viewFilterGroup', - foreignKey: 'viewFilterGroupId', - universalFlatEntityForeignKeyAggregator: - 'viewFilterUniversalIdentifiers', - universalForeignKey: 'viewFilterGroupUniversalIdentifier', - isNullable: true, - }, - workspace: null, - application: null, - }, - oneToMany: {}, - }, - viewGroup: { - manyToOne: { - view: { - metadataName: 'view', - foreignKey: 'viewId', - universalFlatEntityForeignKeyAggregator: - 'viewGroupUniversalIdentifiers', - universalForeignKey: 'viewUniversalIdentifier', - isNullable: false, - }, - workspace: null, - application: null, - }, - oneToMany: {}, - }, - index: { - manyToOne: { - objectMetadata: { - metadataName: 'objectMetadata', - foreignKey: 'objectMetadataId', - universalFlatEntityForeignKeyAggregator: - 'indexMetadataUniversalIdentifiers', - universalForeignKey: 'objectMetadataUniversalIdentifier', - isNullable: false, - }, - workspace: null, - application: null, - }, - oneToMany: { - indexFieldMetadatas: null, - }, - }, - logicFunction: { - manyToOne: { - workspace: null, - application: null, - }, - oneToMany: {}, - }, - role: { - manyToOne: { - workspace: null, - application: null, - }, - oneToMany: { - roleTargets: { metadataName: 'roleTarget' }, - objectPermissions: null, - permissionFlags: null, - fieldPermissions: null, - rowLevelPermissionPredicates: { - metadataName: 'rowLevelPermissionPredicate', - }, - rowLevelPermissionPredicateGroups: { - metadataName: 'rowLevelPermissionPredicateGroup', - }, - }, - }, - roleTarget: { - manyToOne: { - role: { - metadataName: 'role', - foreignKey: 'roleId', - universalFlatEntityForeignKeyAggregator: - 'roleTargetUniversalIdentifiers', - universalForeignKey: 'roleUniversalIdentifier', - isNullable: false, - }, - apiKey: null, - workspace: null, - application: null, - }, - oneToMany: {}, - }, - pageLayout: { - manyToOne: { - workspace: null, - objectMetadata: { - metadataName: 'objectMetadata', - foreignKey: 'objectMetadataId', - universalFlatEntityForeignKeyAggregator: null, - universalForeignKey: 'objectMetadataUniversalIdentifier', - isNullable: true, - }, - application: null, - defaultTabToFocusOnMobileAndSidePanel: { - metadataName: 'pageLayoutTab', - foreignKey: 'defaultTabToFocusOnMobileAndSidePanelId', - universalFlatEntityForeignKeyAggregator: null, - universalForeignKey: - 'defaultTabToFocusOnMobileAndSidePanelUniversalIdentifier', - isNullable: true, - }, - }, - oneToMany: { - tabs: { metadataName: 'pageLayoutTab' }, - }, - }, - pageLayoutTab: { - manyToOne: { - workspace: null, - pageLayout: { - metadataName: 'pageLayout', - foreignKey: 'pageLayoutId', - universalFlatEntityForeignKeyAggregator: 'tabUniversalIdentifiers', - universalForeignKey: 'pageLayoutUniversalIdentifier', - isNullable: false, - }, - application: null, - }, - oneToMany: { - widgets: { metadataName: 'pageLayoutWidget' }, - }, - }, - pageLayoutWidget: { - manyToOne: { - workspace: null, - pageLayoutTab: { - metadataName: 'pageLayoutTab', - foreignKey: 'pageLayoutTabId', - universalFlatEntityForeignKeyAggregator: 'widgetUniversalIdentifiers', - universalForeignKey: 'pageLayoutTabUniversalIdentifier', - isNullable: false, - }, - objectMetadata: { - metadataName: 'objectMetadata', - foreignKey: 'objectMetadataId', - universalFlatEntityForeignKeyAggregator: null, - universalForeignKey: 'objectMetadataUniversalIdentifier', - isNullable: true, - }, - application: null, - }, - oneToMany: {}, - }, - rowLevelPermissionPredicate: { - manyToOne: { - workspace: null, - role: { - metadataName: 'role', - foreignKey: 'roleId', - universalFlatEntityForeignKeyAggregator: null, - universalForeignKey: 'roleUniversalIdentifier', - isNullable: false, - }, - fieldMetadata: { - metadataName: 'fieldMetadata', - foreignKey: 'fieldMetadataId', - universalFlatEntityForeignKeyAggregator: null, - universalForeignKey: 'fieldMetadataUniversalIdentifier', - isNullable: false, - }, - workspaceMemberFieldMetadata: { - metadataName: 'fieldMetadata', - foreignKey: 'workspaceMemberFieldMetadataId', - universalFlatEntityForeignKeyAggregator: null, - universalForeignKey: 'workspaceMemberFieldMetadataUniversalIdentifier', - isNullable: true, - }, - objectMetadata: { - metadataName: 'objectMetadata', - foreignKey: 'objectMetadataId', - universalFlatEntityForeignKeyAggregator: null, - universalForeignKey: 'objectMetadataUniversalIdentifier', - isNullable: false, - }, - rowLevelPermissionPredicateGroup: { - metadataName: 'rowLevelPermissionPredicateGroup', - foreignKey: 'rowLevelPermissionPredicateGroupId', - universalFlatEntityForeignKeyAggregator: null, - universalForeignKey: - 'rowLevelPermissionPredicateGroupUniversalIdentifier', - isNullable: true, - }, - application: null, - }, - oneToMany: {}, - }, - rowLevelPermissionPredicateGroup: { - manyToOne: { - objectMetadata: { - metadataName: 'objectMetadata', - foreignKey: 'objectMetadataId', - universalFlatEntityForeignKeyAggregator: null, - universalForeignKey: 'objectMetadataUniversalIdentifier', - isNullable: false, - }, - role: { - metadataName: 'role', - foreignKey: 'roleId', - universalFlatEntityForeignKeyAggregator: null, - universalForeignKey: 'roleUniversalIdentifier', - isNullable: false, - }, - parentRowLevelPermissionPredicateGroup: { - metadataName: 'rowLevelPermissionPredicateGroup', - foreignKey: 'parentRowLevelPermissionPredicateGroupId', - universalFlatEntityForeignKeyAggregator: - 'childRowLevelPermissionPredicateGroupUniversalIdentifiers', - universalForeignKey: - 'parentRowLevelPermissionPredicateGroupUniversalIdentifier', - isNullable: true, - }, - workspace: null, - application: null, - }, - oneToMany: { - childRowLevelPermissionPredicateGroups: { - metadataName: 'rowLevelPermissionPredicateGroup', - }, - rowLevelPermissionPredicates: { - metadataName: 'rowLevelPermissionPredicate', - }, - }, - }, - viewFilterGroup: { - manyToOne: { - application: null, - parentViewFilterGroup: { - metadataName: 'viewFilterGroup', - foreignKey: 'parentViewFilterGroupId', - universalFlatEntityForeignKeyAggregator: - 'childViewFilterGroupUniversalIdentifiers', - universalForeignKey: 'parentViewFilterGroupUniversalIdentifier', - isNullable: true, - }, - view: { - metadataName: 'view', - foreignKey: 'viewId', - universalFlatEntityForeignKeyAggregator: - 'viewFilterGroupUniversalIdentifiers', - universalForeignKey: 'viewUniversalIdentifier', - isNullable: false, - }, - workspace: null, - }, - oneToMany: { - childViewFilterGroups: { metadataName: 'viewFilterGroup' }, - viewFilters: { metadataName: 'viewFilter' }, - }, - }, - frontComponent: { - manyToOne: { - workspace: null, - application: null, - }, - oneToMany: {}, - }, - webhook: { - manyToOne: { - workspace: null, - application: null, - }, - oneToMany: {}, - }, -} as const satisfies UniversalMetadataRelationsProperties; diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/to-universal-foreign-key.type.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/to-universal-foreign-key.type.ts new file mode 100644 index 00000000000..a0a46c56157 --- /dev/null +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/to-universal-foreign-key.type.ts @@ -0,0 +1,2 @@ +export type ToUniversalForeignKey = + T extends `${infer Prefix}Id` ? `${Prefix}UniversalIdentifier` : never; diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-entity-from.type.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-entity-from.type.ts index d193d2fab35..3a85c717de8 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-entity-from.type.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-entity-from.type.ts @@ -1,7 +1,8 @@ import { type AllMetadataName } from 'twenty-shared/metadata'; import { type FormatRecordSerializedRelationProperties } from 'twenty-shared/types'; -import { type ALL_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-metadata-relations.constant'; +import { type ALL_MANY_TO_ONE_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-many-to-one-metadata-relations.constant'; +import { type ALL_ONE_TO_MANY_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-one-to-many-metadata-relations.constant'; import { type AddSuffixToEntityManyToOneProperties } from 'src/engine/metadata-modules/flat-entity/types/add-suffix-to-entity-many-to-one-properties.type'; import { type AddSuffixToEntityOneToManyProperties } from 'src/engine/metadata-modules/flat-entity/types/add-suffix-to-entity-one-to-many-properties.type'; import { type CastRecordTypeOrmDatePropertiesToString } from 'src/engine/metadata-modules/flat-entity/types/cast-record-typeorm-date-properties-to-string.type'; @@ -46,8 +47,8 @@ export type UniversalFlatEntityFrom< | 'applicationId' | 'workspaceId' | 'id' - | keyof (typeof ALL_METADATA_RELATIONS)[TMetadataName]['manyToOne'] - | keyof (typeof ALL_METADATA_RELATIONS)[TMetadataName]['oneToMany'] + | keyof (typeof ALL_MANY_TO_ONE_METADATA_RELATIONS)[TMetadataName] + | keyof (typeof ALL_ONE_TO_MANY_METADATA_RELATIONS)[TMetadataName] | Extract, keyof TEntity> | keyof CastRecordTypeOrmDatePropertiesToString | AllJsonbPropertiesWithSerializedPropertiesForMetadataName diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-object-metadata.type.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-object-metadata.type.ts index 7c417ae5b1c..8e6ffe1444e 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-object-metadata.type.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-object-metadata.type.ts @@ -4,7 +4,6 @@ import { type UniversalFlatEntityFrom } from 'src/engine/workspace-manager/works export type UniversalFlatObjectMetadata = UniversalFlatEntityFrom< Omit< ObjectMetadataEntity, - | 'targetRelationFields' | 'dataSourceId' | 'labelIdentifierFieldMetadataId' | 'imageIdentifierFieldMetadataId' diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/add-universal-flat-entity-to-universal-flat-entity-and-related-entity-maps-through-mutation-or-throw.util.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/add-universal-flat-entity-to-universal-flat-entity-and-related-entity-maps-through-mutation-or-throw.util.ts index 711ed248d31..0e822604b95 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/add-universal-flat-entity-to-universal-flat-entity-and-related-entity-maps-through-mutation-or-throw.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/add-universal-flat-entity-to-universal-flat-entity-and-related-entity-maps-through-mutation-or-throw.util.ts @@ -1,16 +1,17 @@ import { type AllMetadataName } from 'twenty-shared/metadata'; import { isDefined } from 'twenty-shared/utils'; +import { ALL_MANY_TO_ONE_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-many-to-one-metadata-relations.constant'; +import { ALL_ONE_TO_MANY_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-one-to-many-metadata-relations.constant'; import { FlatEntityMapsException, FlatEntityMapsExceptionCode, } from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception'; -import { type MetadataUniversalFlatEntityAndRelatedUniversalFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/metadata-related-types.type'; import { type MetadataRelatedFlatEntityMapsKeys } from 'src/engine/metadata-modules/flat-entity/types/metadata-related-flat-entity-maps-keys.type'; +import { type MetadataUniversalFlatEntityAndRelatedUniversalFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/metadata-related-types.type'; import { type MetadataUniversalFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-universal-flat-entity.type'; import { findFlatEntityByUniversalIdentifierOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier-or-throw.util'; import { getMetadataFlatEntityMapsKey } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-flat-entity-maps-key.util'; -import { ALL_UNIVERSAL_METADATA_RELATIONS } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/constants/all-universal-metadata-relations.constant'; import { type UniversalFlatEntityMaps } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-entity-maps.type'; import { addUniversalFlatEntityToUniversalFlatEntityMapsThroughMutationOrThrow } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/add-universal-flat-entity-to-universal-flat-entity-maps-through-mutation-or-throw.util'; import { replaceUniversalFlatEntityInUniversalFlatEntityMapsThroughMutationOrThrow } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/replace-universal-flat-entity-in-universal-flat-entity-maps-through-mutation-or-throw.util'; @@ -37,29 +38,46 @@ export const addUniversalFlatEntityToUniversalFlatEntityAndRelatedEntityMapsThro universalFlatEntityAndRelatedMapsToMutate[flatEntityMapsKey], }); - const universalManyToOneRelations = Object.values( - ALL_UNIVERSAL_METADATA_RELATIONS[metadataName].manyToOne, - ) as Array<{ - metadataName: AllMetadataName; - universalFlatEntityForeignKeyAggregator: string | null; - universalForeignKey: string; - } | null>; + const manyToOneRelations = ALL_MANY_TO_ONE_METADATA_RELATIONS[metadataName]; - for (const universalRelation of universalManyToOneRelations) { - if (!isDefined(universalRelation)) { + for (const relationPropertyName of Object.keys(manyToOneRelations)) { + const relation = manyToOneRelations[ + relationPropertyName as keyof typeof manyToOneRelations + ] as { + metadataName: AllMetadataName; + inverseOneToManyProperty: string | null; + universalForeignKey: string; + } | null; + + if (!isDefined(relation)) { continue; } const { metadataName: relatedMetadataName, - universalFlatEntityForeignKeyAggregator, + inverseOneToManyProperty, universalForeignKey, - } = universalRelation; + } = relation; - if (!isDefined(universalFlatEntityForeignKeyAggregator)) { + if (!isDefined(inverseOneToManyProperty)) { continue; } + const oneToManyRelations = + ALL_ONE_TO_MANY_METADATA_RELATIONS[relatedMetadataName]; + + const inverseRelation = oneToManyRelations[ + inverseOneToManyProperty as keyof typeof oneToManyRelations + ] as { + universalFlatEntityForeignKeyAggregator: string; + } | null; + + if (!isDefined(inverseRelation)) { + continue; + } + + const { universalFlatEntityForeignKeyAggregator } = inverseRelation; + const relatedFlatEntityMapsKey = getMetadataFlatEntityMapsKey(relatedMetadataName); diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/delete-universal-flat-entity-from-universal-flat-entity-and-related-entity-maps-through-mutation-or-throw.util.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/delete-universal-flat-entity-from-universal-flat-entity-and-related-entity-maps-through-mutation-or-throw.util.ts index bce96cc57bf..6cdec3004a2 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/delete-universal-flat-entity-from-universal-flat-entity-and-related-entity-maps-through-mutation-or-throw.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/delete-universal-flat-entity-from-universal-flat-entity-and-related-entity-maps-through-mutation-or-throw.util.ts @@ -1,6 +1,8 @@ import { type AllMetadataName } from 'twenty-shared/metadata'; import { isDefined } from 'twenty-shared/utils'; +import { ALL_MANY_TO_ONE_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-many-to-one-metadata-relations.constant'; +import { ALL_ONE_TO_MANY_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-one-to-many-metadata-relations.constant'; import { FlatEntityMapsException, FlatEntityMapsExceptionCode, @@ -10,7 +12,6 @@ import { type MetadataUniversalFlatEntityAndRelatedUniversalFlatEntityMaps } fro import { type MetadataUniversalFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-universal-flat-entity.type'; import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier.util'; import { getMetadataFlatEntityMapsKey } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-flat-entity-maps-key.util'; -import { ALL_UNIVERSAL_METADATA_RELATIONS } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/constants/all-universal-metadata-relations.constant'; import { type UniversalFlatEntityMaps } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-entity-maps.type'; import { deleteUniversalFlatEntityFromUniversalFlatEntityMapsThroughMutationOrThrow } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/delete-universal-flat-entity-from-universal-flat-entity-maps-through-mutation-or-throw.util'; import { replaceUniversalFlatEntityInUniversalFlatEntityMapsThroughMutationOrThrow } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/replace-universal-flat-entity-in-universal-flat-entity-maps-through-mutation-or-throw.util'; @@ -41,29 +42,46 @@ export const deleteUniversalFlatEntityFromUniversalFlatEntityAndRelatedEntityMap >, }); - const universalManyToOneRelations = Object.values( - ALL_UNIVERSAL_METADATA_RELATIONS[metadataName].manyToOne, - ) as Array<{ - metadataName: AllMetadataName; - universalFlatEntityForeignKeyAggregator: string | null; - universalForeignKey: string; - } | null>; + const manyToOneRelations = ALL_MANY_TO_ONE_METADATA_RELATIONS[metadataName]; - for (const universalRelation of universalManyToOneRelations) { - if (!isDefined(universalRelation)) { + for (const relationPropertyName of Object.keys(manyToOneRelations)) { + const relation = manyToOneRelations[ + relationPropertyName as keyof typeof manyToOneRelations + ] as { + metadataName: AllMetadataName; + inverseOneToManyProperty: string | null; + universalForeignKey: string; + } | null; + + if (!isDefined(relation)) { continue; } const { metadataName: relatedMetadataName, - universalFlatEntityForeignKeyAggregator, + inverseOneToManyProperty, universalForeignKey, - } = universalRelation; + } = relation; - if (!isDefined(universalFlatEntityForeignKeyAggregator)) { + if (!isDefined(inverseOneToManyProperty)) { continue; } + const oneToManyRelations = + ALL_ONE_TO_MANY_METADATA_RELATIONS[relatedMetadataName]; + + const inverseRelation = oneToManyRelations[ + inverseOneToManyProperty as keyof typeof oneToManyRelations + ] as { + universalFlatEntityForeignKeyAggregator: string; + } | null; + + if (!isDefined(inverseRelation)) { + continue; + } + + const { universalFlatEntityForeignKeyAggregator } = inverseRelation; + const relatedFlatEntityMapsKey = getMetadataFlatEntityMapsKey(relatedMetadataName); diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-universal-relation-identifiers-to-ids.util.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-universal-relation-identifiers-to-ids.util.ts index 607b53a0111..17c809330a0 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-universal-relation-identifiers-to-ids.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-universal-relation-identifiers-to-ids.util.ts @@ -2,44 +2,38 @@ import { t } from '@lingui/core/macro'; import { type AllMetadataName } from 'twenty-shared/metadata'; import { isDefined } from 'twenty-shared/utils'; -import { type MetadataManyToOneRelationConfiguration } from 'src/engine/metadata-modules/flat-entity/constant/all-metadata-relations.constant'; +import { ALL_MANY_TO_ONE_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-many-to-one-metadata-relations.constant'; import { FlatEntityMapsException, FlatEntityMapsExceptionCode, } from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception'; import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type'; -import { type ExtractEntityManyToOneEntityRelationProperties } from 'src/engine/metadata-modules/flat-entity/types/extract-entity-many-to-one-entity-relation-properties.type'; -import { type MetadataEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-entity.type'; import { type MetadataFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-flat-entity.type'; import { type MetadataToFlatEntityMapsKey } from 'src/engine/metadata-modules/flat-entity/types/metadata-to-flat-entity-maps-key'; import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier.util'; import { getMetadataFlatEntityMapsKey } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-flat-entity-maps-key.util'; -import { - ALL_UNIVERSAL_METADATA_RELATIONS, - type ToUniversalMetadataManyToOneRelationConfiguration, -} from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/constants/all-universal-metadata-relations.constant'; -type UniversalManyToOneConfig = - (typeof ALL_UNIVERSAL_METADATA_RELATIONS)[T]['manyToOne']; +type ManyToOneRelationsConfig = + (typeof ALL_MANY_TO_ONE_METADATA_RELATIONS)[T]; type ExtractUniversalForeignKeys = { [K in keyof T]: T[K] extends { universalForeignKey: infer UFK } ? UFK : never; }[keyof T]; type MetadataUniversalManyToOneJoinColumn = - ExtractUniversalForeignKeys>; + ExtractUniversalForeignKeys>; type TargetMetadataNamesForUniversalForeignKeys< T extends AllMetadataName, TProvidedKeys extends string, > = { - [K in keyof UniversalManyToOneConfig]: UniversalManyToOneConfig[K] extends { + [K in keyof ManyToOneRelationsConfig]: ManyToOneRelationsConfig[K] extends { universalForeignKey: infer _UFK extends TProvidedKeys; metadataName: infer MN extends AllMetadataName; } ? MN : never; -}[keyof UniversalManyToOneConfig]; +}[keyof ManyToOneRelationsConfig]; type RequiredFlatEntityMapsForUniversalForeignKeys< T extends AllMetadataName, @@ -58,14 +52,14 @@ type ResolvedForeignKeyIds< string >, > = { - [K in keyof UniversalManyToOneConfig as UniversalManyToOneConfig[K] extends { + [K in keyof ManyToOneRelationsConfig as ManyToOneRelationsConfig[K] extends { universalForeignKey: infer UFK extends string; foreignKey: infer FK extends string; } ? UFK extends TProvidedKeys ? FK : never - : never]: UniversalManyToOneConfig[K] extends { isNullable: true } + : never]: ManyToOneRelationsConfig[K] extends { isNullable: true } ? string | null : string; }; @@ -88,18 +82,20 @@ export const resolveUniversalRelationIdentifiersToIds = < TProvidedKeys >; }): ResolvedForeignKeyIds => { - const relations = ALL_UNIVERSAL_METADATA_RELATIONS[metadataName].manyToOne; + const relationEntries = ALL_MANY_TO_ONE_METADATA_RELATIONS[metadataName]; const result: Record = {}; - for (const relation of Object.values( - relations, - ) as ToUniversalMetadataManyToOneRelationConfiguration< - MetadataManyToOneRelationConfiguration< - T, - ExtractEntityManyToOneEntityRelationProperties> - > - >[]) { - if (!isDefined(relation)) { + for (const relationPropertyName of Object.keys(relationEntries)) { + const relationEntry = relationEntries[ + relationPropertyName as keyof typeof relationEntries + ] as { + foreignKey: string; + metadataName: AllMetadataName; + isNullable: boolean; + universalForeignKey: string; + } | null; + + if (!isDefined(relationEntry)) { continue; } @@ -108,7 +104,7 @@ export const resolveUniversalRelationIdentifiersToIds = < universalForeignKey, metadataName: targetMetadataName, isNullable, - } = relation; + } = relationEntry; if ( !Object.prototype.hasOwnProperty.call( diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-universal-update-relation-identifiers-to-ids.util.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-universal-update-relation-identifiers-to-ids.util.ts index 084f061cc28..28599ee6e1f 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-universal-update-relation-identifiers-to-ids.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-universal-update-relation-identifiers-to-ids.util.ts @@ -1,22 +1,16 @@ import { type AllMetadataName } from 'twenty-shared/metadata'; import { isDefined } from 'twenty-shared/utils'; -import { type MetadataManyToOneRelationConfiguration } from 'src/engine/metadata-modules/flat-entity/constant/all-metadata-relations.constant'; +import { ALL_MANY_TO_ONE_METADATA_RELATIONS } from 'src/engine/metadata-modules/flat-entity/constant/all-many-to-one-metadata-relations.constant'; import { FlatEntityMapsException, FlatEntityMapsExceptionCode, } from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception'; import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type'; -import { type ExtractEntityManyToOneEntityRelationProperties } from 'src/engine/metadata-modules/flat-entity/types/extract-entity-many-to-one-entity-relation-properties.type'; import { type FlatEntityUpdate } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-update.type'; -import { type MetadataEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-entity.type'; import { type MetadataFlatEntity } from 'src/engine/metadata-modules/flat-entity/types/metadata-flat-entity.type'; import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier.util'; import { getMetadataFlatEntityMapsKey } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-flat-entity-maps-key.util'; -import { - ALL_UNIVERSAL_METADATA_RELATIONS, - type ToUniversalMetadataManyToOneRelationConfiguration, -} from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/constants/all-universal-metadata-relations.constant'; import { type UniversalFlatEntityUpdate } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-entity-update.type'; export const resolveUniversalUpdateRelationIdentifiersToIds = < @@ -30,17 +24,18 @@ export const resolveUniversalUpdateRelationIdentifiersToIds = < universalUpdate: UniversalFlatEntityUpdate; allFlatEntityMaps: AllFlatEntityMaps; }): FlatEntityUpdate => { - const relations = ALL_UNIVERSAL_METADATA_RELATIONS[metadataName].manyToOne; + const relationEntries = ALL_MANY_TO_ONE_METADATA_RELATIONS[metadataName]; const result: Record = { ...universalUpdate }; - for (const relation of Object.values( - relations, - ) as ToUniversalMetadataManyToOneRelationConfiguration< - MetadataManyToOneRelationConfiguration< - T, - ExtractEntityManyToOneEntityRelationProperties> - > - >[]) { + for (const relationPropertyName of Object.keys(relationEntries)) { + const relation = relationEntries[ + relationPropertyName as keyof typeof relationEntries + ] as { + foreignKey: string; + universalForeignKey: string; + metadataName: AllMetadataName; + } | null; + if (!isDefined(relation)) { continue; } diff --git a/packages/twenty-server/src/utils/__test__/get-object-metadata-entity.mock.ts b/packages/twenty-server/src/utils/__test__/get-object-metadata-entity.mock.ts index 0efc2140301..79bbc4ddfb4 100644 --- a/packages/twenty-server/src/utils/__test__/get-object-metadata-entity.mock.ts +++ b/packages/twenty-server/src/utils/__test__/get-object-metadata-entity.mock.ts @@ -46,7 +46,6 @@ export const getMockObjectMetadataEntity = ( universalIdentifier: faker.string.uuid(), applicationId: faker.string.uuid(), application: {} as ApplicationEntity, - targetRelationFields: [], standardOverrides: null, targetTableName: faker.string.uuid(), views: [], 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 7a88499af87..bb72264e09f 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 @@ -79,8 +79,6 @@ exports[`syncApplication should return workspace migration actions on initial sy "icon": null, "isEditable": true, "label": "Test Role", - "rowLevelPermissionPredicateGroupUniversalIdentifiers": [], - "rowLevelPermissionPredicateUniversalIdentifiers": [], "universalIdentifier": Any, "updatedAt": Any, }, @@ -123,8 +121,6 @@ exports[`syncApplication should return workspace migration actions on initial sy "icon": null, "isEditable": true, "label": "Viewer Role", - "rowLevelPermissionPredicateGroupUniversalIdentifiers": [], - "rowLevelPermissionPredicateUniversalIdentifiers": [], "universalIdentifier": Any, "updatedAt": Any, },