[BREAKING_CHANGE_NESTED_WORKSPACE]Refactor FlatEntity typing in aim of introducing UniversalFlatEntity (#16701)

# Introduction
Added a `WorkspaceRelated` and `AllNonWorkspaceRelatedEntity` to
simplify the `FlatEntityFrom` that now do not expect a string literal to
omit and itself builds the related many to one entities foreign key
aggregators

We now have the type grain over relation to syncable or just workspace
related entities

Added a migrations that sets the fk on missing entities

## Next
In upcoming PR we will be able to introduce such below type
```ts
import { type CastRecordTypeOrmDatePropertiesToString } from 'src/engine/metadata-modules/flat-entity/types/cast-record-typeorm-date-properties-to-string.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 ExtractEntityRelatedEntityProperties } from 'src/engine/metadata-modules/flat-entity/types/extract-entity-related-entity-properties.type';
import { type RemoveSuffix } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/remove-suffix.type';
import { type SyncableEntity } from 'src/engine/workspace-manager/workspace-sync/types/syncable-entity.interface';

export type UniversalFlatEntityFrom<TEntity extends SyncableEntity> = Omit<
  TEntity,
  | `${ExtractEntityManyToOneEntityRelationProperties<TEntity> & string}Id`
  | ExtractEntityRelatedEntityProperties<TEntity>
  | 'application'
  | 'workspaceId'
  | 'applicationId'
  | keyof CastRecordTypeOrmDatePropertiesToString<TEntity>
> &
  CastRecordTypeOrmDatePropertiesToString<TEntity> & {
    [P in ExtractEntityManyToOneEntityRelationProperties<TEntity> &
      string as `${RemoveSuffix<P, 's'>}UniversalIdentifier`]: string;
  } & {
    [P in ExtractEntityOneToManyEntityRelationProperties<
      TEntity,
      SyncableEntity
    > &
      string as `${RemoveSuffix<P, 's'>}UniversalIdentifiers`]: string[];
  };

```
This commit is contained in:
Paul Rastoin
2025-12-30 14:47:02 +01:00
committed by GitHub
parent 720348c583
commit e3ffdb0c2b
101 changed files with 559 additions and 874 deletions
@@ -1,12 +0,0 @@
import { type Relation } from 'typeorm';
export type ExtractRecordTypeOrmRelationProperties<T, TRelationTargets> =
NonNullable<
{
[P in keyof T]: NonNullable<T[P]> extends Relation<
TRelationTargets | TRelationTargets[]
>
? P
: never;
}[keyof T]
>;
@@ -1,20 +0,0 @@
import { type ApplicationEntity } from 'src/engine/core-modules/application/application.entity';
import { type DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity';
import { type FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { type IndexFieldMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-field-metadata.entity';
import { type IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity';
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { type FieldPermissionEntity } from 'src/engine/metadata-modules/object-permission/field-permission/field-permission.entity';
import { type ObjectPermissionEntity } from 'src/engine/metadata-modules/object-permission/object-permission.entity';
import { type ViewEntity } from 'src/engine/metadata-modules/view/entities/view.entity';
export type MetadataEntitiesRelationTarget =
| ObjectMetadataEntity
| FieldMetadataEntity
| IndexFieldMetadataEntity
| FieldPermissionEntity
| DataSourceEntity
| ApplicationEntity
| IndexMetadataEntity
| ObjectPermissionEntity
| ViewEntity;
@@ -0,0 +1,4 @@
export type RemoveSuffix<
T extends string,
P extends string,
> = T extends `${infer Prefix}${P}` ? Prefix : T;
@@ -10,6 +10,7 @@ export type PartialIndexMetadata = Omit<
| 'universalIdentifier'
| 'application'
| 'applicationId'
| 'workspace'
> & {
columns: string[];
};
@@ -0,0 +1,41 @@
import { type ApplicationVariableEntity } from 'src/engine/core-modules/applicationVariable/application-variable.entity';
import { type BillingMeterEntity } from 'src/engine/core-modules/billing/entities/billing-meter.entity';
import { type BillingPriceEntity } from 'src/engine/core-modules/billing/entities/billing-price.entity';
import { type BillingProductEntity } from 'src/engine/core-modules/billing/entities/billing-product.entity';
import { type BillingSubscriptionItemEntity } from 'src/engine/core-modules/billing/entities/billing-subscription-item.entity';
import { type TwoFactorAuthenticationMethodEntity } from 'src/engine/core-modules/two-factor-authentication/entities/two-factor-authentication-method.entity';
import { type UserEntity } from 'src/engine/core-modules/user/user.entity';
import { type WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { type AgentMessagePartEntity } from 'src/engine/metadata-modules/ai/ai-agent-execution/entities/agent-message-part.entity';
import { type AgentMessageEntity } from 'src/engine/metadata-modules/ai/ai-agent-execution/entities/agent-message.entity';
import { type AgentTurnEntity } from 'src/engine/metadata-modules/ai/ai-agent-execution/entities/agent-turn.entity';
import { type AgentTurnEvaluationEntity } from 'src/engine/metadata-modules/ai/ai-agent-monitor/entities/agent-turn-evaluation.entity';
import { type AgentChatThreadEntity } from 'src/engine/metadata-modules/ai/ai-chat/entities/agent-chat-thread.entity';
import { type IndexFieldMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-field-metadata.entity';
/**
* Union of all entities that don't have a direct `workspaceId` field
* (i.e., they don't extend `WorkspaceRelatedEntity`).
*
* This type is used alongside `WorkspaceRelatedEntity` to enable TypeScript
* to properly extract entity relation properties for dynamic typing purposes.
*
* @see ExtractEntityRelatedEntityProperties
* @see ExtractEntityManyToOneEntityRelationProperties
* @see ExtractEntityOneToManyEntityRelationProperties
*/
export type AllNonWorkspaceRelatedEntity =
| AgentChatThreadEntity
| AgentMessagePartEntity
| AgentMessageEntity
| AgentTurnEntity
| AgentTurnEvaluationEntity
| IndexFieldMetadataEntity
| ApplicationVariableEntity
| BillingMeterEntity
| BillingPriceEntity
| BillingProductEntity
| BillingSubscriptionItemEntity
| TwoFactorAuthenticationMethodEntity
| UserEntity
| WorkspaceEntity;
@@ -1,11 +1,12 @@
import { Column, Index, JoinColumn, ManyToOne, Relation } from 'typeorm';
import type { ApplicationEntity } from 'src/engine/core-modules/application/application.entity';
import { WorkspaceRelatedEntity } from 'src/engine/workspace-manager/workspace-sync/types/workspace-related-entity';
@Index(['workspaceId', 'universalIdentifier'], {
unique: true,
})
export abstract class SyncableEntity {
export abstract class SyncableEntity extends WorkspaceRelatedEntity {
@Column({ nullable: true, type: 'uuid' })
// TODO should not be nullable
universalIdentifier: string;
@@ -0,0 +1,12 @@
import { Column, JoinColumn, ManyToOne, Relation } from 'typeorm';
import type { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
export abstract class WorkspaceRelatedEntity {
@Column({ nullable: false, type: 'uuid' })
workspaceId: string;
@ManyToOne('WorkspaceEntity', { onDelete: 'CASCADE' })
@JoinColumn({ name: 'workspaceId' })
workspace: Relation<WorkspaceEntity>;
}