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 a237a3345c..206bc12303 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 d91de3d87f..fc2eb1c7d4 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 75cc0466af..cfbc818ef3 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 a49157dd29..a48ffa68a5 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 0000000000..f6452789c2 --- /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 0000000000..aa65e74529 --- /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 fd78e4367a..0000000000 --- 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 a47821be50..2e251d60e1 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 0000000000..8a9a403ee4 --- /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 0000000000..13cf63ef70 --- /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 214955e24c..e4a09ecd95 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 3cd2f0b924..3b256b6a66 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 94abeb08cf..8afb6267ea 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 13b295d498..b0412fefd4 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 ba7d88b8a9..065ccb2465 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 706f1f5d3b..9475eb6659 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 7b50f59109..d2ee3e3df0 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 34e83528c6..a48e16c674 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 0e5b5aade3..92df587669 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 0369e7aa80..61eee25e0d 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 db1f6222fc..42e716ce4f 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 3dcaf7eaf2..44e657efce 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 575d3b59ac..27db7ad710 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 00a4e3f0db..2393dd82a4 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 a3c7a792dd..d1aa610ce9 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 0bd9ccce7b..419e89c948 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 ae6581c9fe..0000000000 --- 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 0000000000..a0a46c5615 --- /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 d193d2fab3..3a85c717de 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 7c417ae5b1..8e6ffe1444 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 711ed248d3..0e822604b9 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 bce96cc57b..6cdec3004a 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 607b53a011..17c809330a 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 084f061cc2..28599ee6e1 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 0efc214030..79bbc4ddfb 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 7a88499af8..bb72264e09 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, },