feat: add targetFieldMetadataId and migration script for relations (#9793)
Fix https://github.com/twentyhq/core-team-issues/issues/238 and https://github.com/twentyhq/core-team-issues/issues/239
This commit is contained in:
@ -0,0 +1,140 @@
|
|||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
|
||||||
|
import chalk from 'chalk';
|
||||||
|
import { Command } from 'nest-commander';
|
||||||
|
import { FieldMetadataType } from 'twenty-shared';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
|
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ActiveWorkspacesCommandOptions,
|
||||||
|
ActiveWorkspacesCommandRunner,
|
||||||
|
} from 'src/database/commands/active-workspaces.command';
|
||||||
|
import { isCommandLogger } from 'src/database/commands/logger';
|
||||||
|
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
|
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||||
|
import {
|
||||||
|
deduceRelationDirection,
|
||||||
|
RelationDirection,
|
||||||
|
} from 'src/engine/utils/deduce-relation-direction.util';
|
||||||
|
|
||||||
|
@Command({
|
||||||
|
name: 'upgrade-0.41:migrate-relations-to-field-metadata',
|
||||||
|
description: 'Migrate relations to field metadata',
|
||||||
|
})
|
||||||
|
export class MigrateRelationsToFieldMetadataCommand extends ActiveWorkspacesCommandRunner {
|
||||||
|
constructor(
|
||||||
|
@InjectRepository(Workspace, 'core')
|
||||||
|
protected readonly workspaceRepository: Repository<Workspace>,
|
||||||
|
@InjectRepository(FieldMetadataEntity, 'metadata')
|
||||||
|
private readonly fieldMetadataRepository: Repository<FieldMetadataEntity>,
|
||||||
|
) {
|
||||||
|
super(workspaceRepository);
|
||||||
|
}
|
||||||
|
|
||||||
|
async executeActiveWorkspacesCommand(
|
||||||
|
_passedParam: string[],
|
||||||
|
options: ActiveWorkspacesCommandOptions,
|
||||||
|
workspaceIds: string[],
|
||||||
|
): Promise<void> {
|
||||||
|
this.logger.log('Running command to create many to one relations');
|
||||||
|
|
||||||
|
if (isCommandLogger(this.logger)) {
|
||||||
|
this.logger.setVerbose(options.verbose ?? false);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (const [index, workspaceId] of workspaceIds.entries()) {
|
||||||
|
await this.processWorkspace(workspaceId, index, workspaceIds.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.log(chalk.green('Command completed!'));
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.log(chalk.red('Error in workspace'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async processWorkspace(
|
||||||
|
workspaceId: string,
|
||||||
|
index: number,
|
||||||
|
total: number,
|
||||||
|
): Promise<void> {
|
||||||
|
try {
|
||||||
|
this.logger.log(
|
||||||
|
`Running command for workspace ${workspaceId} ${index + 1}/${total}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
const fieldMetadataCollection = (await this.fieldMetadataRepository.find({
|
||||||
|
where: { workspaceId, type: FieldMetadataType.RELATION },
|
||||||
|
relations: ['fromRelationMetadata', 'toRelationMetadata'],
|
||||||
|
})) as unknown as FieldMetadataEntity<FieldMetadataType.RELATION>[];
|
||||||
|
|
||||||
|
if (!fieldMetadataCollection.length) {
|
||||||
|
this.logger.log(
|
||||||
|
chalk.yellow(
|
||||||
|
`No relation field metadata found for workspace ${workspaceId}.`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fieldMetadataToUpdateCollection = fieldMetadataCollection.map(
|
||||||
|
(fieldMetadata) => this.mapFieldMetadata(fieldMetadata),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (fieldMetadataToUpdateCollection.length > 0) {
|
||||||
|
await this.fieldMetadataRepository.save(
|
||||||
|
fieldMetadataToUpdateCollection,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.log(
|
||||||
|
chalk.green(`Command completed for workspace ${workspaceId}.`),
|
||||||
|
);
|
||||||
|
} catch {
|
||||||
|
this.logger.log(chalk.red(`Error in workspace ${workspaceId}.`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private mapFieldMetadata(
|
||||||
|
fieldMetadata: FieldMetadataEntity<FieldMetadataType.RELATION>,
|
||||||
|
): FieldMetadataEntity<FieldMetadataType.RELATION> {
|
||||||
|
const relationMetadata =
|
||||||
|
fieldMetadata.fromRelationMetadata ?? fieldMetadata.toRelationMetadata;
|
||||||
|
|
||||||
|
const relationDirection = deduceRelationDirection(
|
||||||
|
fieldMetadata,
|
||||||
|
relationMetadata,
|
||||||
|
);
|
||||||
|
let relationType = relationMetadata.relationType as unknown as RelationType;
|
||||||
|
|
||||||
|
if (
|
||||||
|
relationDirection === RelationDirection.TO &&
|
||||||
|
relationType === RelationType.ONE_TO_MANY
|
||||||
|
) {
|
||||||
|
relationType = RelationType.MANY_TO_ONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
const relationTargetFieldMetadataId =
|
||||||
|
relationDirection === RelationDirection.FROM
|
||||||
|
? relationMetadata.toFieldMetadataId
|
||||||
|
: relationMetadata.fromFieldMetadataId;
|
||||||
|
|
||||||
|
const relationTargetObjectMetadataId =
|
||||||
|
relationDirection === RelationDirection.FROM
|
||||||
|
? relationMetadata.toObjectMetadataId
|
||||||
|
: relationMetadata.fromObjectMetadataId;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...fieldMetadata,
|
||||||
|
settings: {
|
||||||
|
relationType,
|
||||||
|
onDelete: relationMetadata.onDeleteAction,
|
||||||
|
},
|
||||||
|
relationTargetFieldMetadataId,
|
||||||
|
relationTargetObjectMetadataId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,6 +5,7 @@ import { Repository } from 'typeorm';
|
|||||||
|
|
||||||
import { ActiveWorkspacesCommandRunner } from 'src/database/commands/active-workspaces.command';
|
import { ActiveWorkspacesCommandRunner } from 'src/database/commands/active-workspaces.command';
|
||||||
import { BaseCommandOptions } from 'src/database/commands/base.command';
|
import { BaseCommandOptions } from 'src/database/commands/base.command';
|
||||||
|
import { MigrateRelationsToFieldMetadataCommand } from 'src/database/commands/upgrade-version/0-41/0-41-migrate-relations-to-field-metadata.command';
|
||||||
import { SeedWorkflowViewsCommand } from 'src/database/commands/upgrade-version/0-41/0-41-seed-workflow-views.command';
|
import { SeedWorkflowViewsCommand } from 'src/database/commands/upgrade-version/0-41/0-41-seed-workflow-views.command';
|
||||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
import { SyncWorkspaceMetadataCommand } from 'src/engine/workspace-manager/workspace-sync-metadata/commands/sync-workspace-metadata.command';
|
import { SyncWorkspaceMetadataCommand } from 'src/engine/workspace-manager/workspace-sync-metadata/commands/sync-workspace-metadata.command';
|
||||||
@ -19,6 +20,7 @@ export class UpgradeTo0_41Command extends ActiveWorkspacesCommandRunner {
|
|||||||
protected readonly workspaceRepository: Repository<Workspace>,
|
protected readonly workspaceRepository: Repository<Workspace>,
|
||||||
private readonly seedWorkflowViewsCommand: SeedWorkflowViewsCommand,
|
private readonly seedWorkflowViewsCommand: SeedWorkflowViewsCommand,
|
||||||
private readonly syncWorkspaceMetadataCommand: SyncWorkspaceMetadataCommand,
|
private readonly syncWorkspaceMetadataCommand: SyncWorkspaceMetadataCommand,
|
||||||
|
private readonly migrateRelationsToFieldMetadata: MigrateRelationsToFieldMetadataCommand,
|
||||||
) {
|
) {
|
||||||
super(workspaceRepository);
|
super(workspaceRepository);
|
||||||
}
|
}
|
||||||
@ -44,5 +46,11 @@ export class UpgradeTo0_41Command extends ActiveWorkspacesCommandRunner {
|
|||||||
options,
|
options,
|
||||||
workspaceIds,
|
workspaceIds,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await this.migrateRelationsToFieldMetadata.executeActiveWorkspacesCommand(
|
||||||
|
passedParam,
|
||||||
|
options,
|
||||||
|
workspaceIds,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,13 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
|
||||||
|
import { MigrateRelationsToFieldMetadataCommand } from 'src/database/commands/upgrade-version/0-41/0-41-migrate-relations-to-field-metadata.command';
|
||||||
import { SeedWorkflowViewsCommand } from 'src/database/commands/upgrade-version/0-41/0-41-seed-workflow-views.command';
|
import { SeedWorkflowViewsCommand } from 'src/database/commands/upgrade-version/0-41/0-41-seed-workflow-views.command';
|
||||||
import { UpgradeTo0_41Command } from 'src/database/commands/upgrade-version/0-41/0-41-upgrade-version.command';
|
import { UpgradeTo0_41Command } from 'src/database/commands/upgrade-version/0-41/0-41-upgrade-version.command';
|
||||||
import { TypeORMModule } from 'src/database/typeorm/typeorm.module';
|
import { TypeORMModule } from 'src/database/typeorm/typeorm.module';
|
||||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
|
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
|
||||||
|
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||||
import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-metadata.module';
|
import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-metadata.module';
|
||||||
import { WorkspaceHealthModule } from 'src/engine/workspace-manager/workspace-health/workspace-health.module';
|
import { WorkspaceHealthModule } from 'src/engine/workspace-manager/workspace-health/workspace-health.module';
|
||||||
import { SyncWorkspaceLoggerService } from 'src/engine/workspace-manager/workspace-sync-metadata/commands/services/sync-workspace-logger.service';
|
import { SyncWorkspaceLoggerService } from 'src/engine/workspace-manager/workspace-sync-metadata/commands/services/sync-workspace-logger.service';
|
||||||
@ -16,6 +18,7 @@ import { WorkspaceSyncMetadataModule } from 'src/engine/workspace-manager/worksp
|
|||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
TypeOrmModule.forFeature([Workspace], 'core'),
|
TypeOrmModule.forFeature([Workspace], 'core'),
|
||||||
|
TypeOrmModule.forFeature([FieldMetadataEntity], 'metadata'),
|
||||||
TypeORMModule,
|
TypeORMModule,
|
||||||
DataSourceModule,
|
DataSourceModule,
|
||||||
ObjectMetadataModule,
|
ObjectMetadataModule,
|
||||||
@ -28,6 +31,7 @@ import { WorkspaceSyncMetadataModule } from 'src/engine/workspace-manager/worksp
|
|||||||
SyncWorkspaceMetadataCommand,
|
SyncWorkspaceMetadataCommand,
|
||||||
SeedWorkflowViewsCommand,
|
SeedWorkflowViewsCommand,
|
||||||
UpgradeTo0_41Command,
|
UpgradeTo0_41Command,
|
||||||
|
MigrateRelationsToFieldMetadataCommand,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class UpgradeTo0_41CommandModule {}
|
export class UpgradeTo0_41CommandModule {}
|
||||||
|
|||||||
@ -0,0 +1,55 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||||
|
|
||||||
|
export class AddRelationTargetFieldAndObjectToFieldMetadata1737561084251
|
||||||
|
implements MigrationInterface
|
||||||
|
{
|
||||||
|
name = 'AddRelationTargetFieldAndObjectToFieldMetadata1737561084251';
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "metadata"."fieldMetadata" ADD "relationTargetFieldMetadataId" uuid`,
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "metadata"."fieldMetadata" ADD CONSTRAINT "UQ_47a6c57e1652b6475f8248cff78" UNIQUE ("relationTargetFieldMetadataId")`,
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "metadata"."fieldMetadata" ADD "relationTargetObjectMetadataId" uuid`,
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`CREATE INDEX "IndexOnRelationTargetObjectMetadataId" ON "metadata"."fieldMetadata" ("relationTargetObjectMetadataId") `,
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`CREATE INDEX "IndexOnRelationTargetFieldMetadataId" ON "metadata"."fieldMetadata" ("relationTargetFieldMetadataId") `,
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "metadata"."fieldMetadata" ADD CONSTRAINT "FK_47a6c57e1652b6475f8248cff78" FOREIGN KEY ("relationTargetFieldMetadataId") REFERENCES "metadata"."fieldMetadata"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "metadata"."fieldMetadata" ADD CONSTRAINT "FK_6f6c87ec32cca956d8be321071c" FOREIGN KEY ("relationTargetObjectMetadataId") REFERENCES "metadata"."objectMetadata"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "metadata"."fieldMetadata" DROP CONSTRAINT "FK_6f6c87ec32cca956d8be321071c"`,
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "metadata"."fieldMetadata" DROP CONSTRAINT "FK_47a6c57e1652b6475f8248cff78"`,
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`DROP INDEX "metadata"."IndexOnRelationTargetFieldMetadataId"`,
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`DROP INDEX "metadata"."IndexOnRelationTargetObjectMetadataId"`,
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "metadata"."fieldMetadata" DROP COLUMN "relationTargetObjectMetadataId"`,
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "metadata"."fieldMetadata" DROP CONSTRAINT "UQ_47a6c57e1652b6475f8248cff78"`,
|
||||||
|
);
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "metadata"."fieldMetadata" DROP COLUMN "relationTargetFieldMetadataId"`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,6 +3,7 @@ import {
|
|||||||
Column,
|
Column,
|
||||||
CreateDateColumn,
|
CreateDateColumn,
|
||||||
Entity,
|
Entity,
|
||||||
|
Index,
|
||||||
JoinColumn,
|
JoinColumn,
|
||||||
ManyToOne,
|
ManyToOne,
|
||||||
OneToMany,
|
OneToMany,
|
||||||
@ -28,6 +29,12 @@ import { RelationMetadataEntity } from 'src/engine/metadata-modules/relation-met
|
|||||||
'objectMetadataId',
|
'objectMetadataId',
|
||||||
'workspaceId',
|
'workspaceId',
|
||||||
])
|
])
|
||||||
|
@Index('IndexOnRelationTargetFieldMetadataId', [
|
||||||
|
'relationTargetFieldMetadataId',
|
||||||
|
])
|
||||||
|
@Index('IndexOnRelationTargetObjectMetadataId', [
|
||||||
|
'relationTargetObjectMetadataId',
|
||||||
|
])
|
||||||
export class FieldMetadataEntity<
|
export class FieldMetadataEntity<
|
||||||
T extends FieldMetadataType | 'default' = 'default',
|
T extends FieldMetadataType | 'default' = 'default',
|
||||||
> implements FieldMetadataInterface<T>
|
> implements FieldMetadataInterface<T>
|
||||||
@ -95,6 +102,26 @@ export class FieldMetadataEntity<
|
|||||||
@Column({ default: false })
|
@Column({ default: false })
|
||||||
isLabelSyncedWithName: boolean;
|
isLabelSyncedWithName: boolean;
|
||||||
|
|
||||||
|
@Column({ nullable: true, type: 'uuid' })
|
||||||
|
relationTargetFieldMetadataId: string;
|
||||||
|
@OneToOne(
|
||||||
|
() => FieldMetadataEntity,
|
||||||
|
(fieldMetadata: FieldMetadataEntity) =>
|
||||||
|
fieldMetadata.relationTargetFieldMetadataId,
|
||||||
|
)
|
||||||
|
@JoinColumn({ name: 'relationTargetFieldMetadataId' })
|
||||||
|
relationTargetFieldMetadata: Relation<FieldMetadataEntity>;
|
||||||
|
|
||||||
|
@Column({ nullable: true, type: 'uuid' })
|
||||||
|
relationTargetObjectMetadataId: string;
|
||||||
|
@ManyToOne(
|
||||||
|
() => ObjectMetadataEntity,
|
||||||
|
(objectMetadata: ObjectMetadataEntity) =>
|
||||||
|
objectMetadata.targetRelationFields,
|
||||||
|
)
|
||||||
|
@JoinColumn({ name: 'relationTargetObjectMetadataId' })
|
||||||
|
relationTargetObjectMetadata: Relation<ObjectMetadataEntity>;
|
||||||
|
|
||||||
@OneToOne(
|
@OneToOne(
|
||||||
() => RelationMetadataEntity,
|
() => RelationMetadataEntity,
|
||||||
(relation: RelationMetadataEntity) => relation.fromFieldMetadata,
|
(relation: RelationMetadataEntity) => relation.fromFieldMetadata,
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
import { FieldMetadataType } from 'twenty-shared';
|
import { FieldMetadataType } from 'twenty-shared';
|
||||||
|
|
||||||
|
import { RelationOnDeleteAction } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-on-delete-action.interface';
|
||||||
|
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
|
||||||
|
|
||||||
export enum NumberDataType {
|
export enum NumberDataType {
|
||||||
FLOAT = 'float',
|
FLOAT = 'float',
|
||||||
INT = 'int',
|
INT = 'int',
|
||||||
@ -30,11 +33,17 @@ export type FieldMetadataDateTimeSettings = {
|
|||||||
displayAsRelativeDate?: boolean;
|
displayAsRelativeDate?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type FieldMetadataRelationSettings = {
|
||||||
|
relationType: RelationType;
|
||||||
|
onDelete?: RelationOnDeleteAction;
|
||||||
|
};
|
||||||
|
|
||||||
type FieldMetadataSettingsMapping = {
|
type FieldMetadataSettingsMapping = {
|
||||||
[FieldMetadataType.NUMBER]: FieldMetadataNumberSettings;
|
[FieldMetadataType.NUMBER]: FieldMetadataNumberSettings;
|
||||||
[FieldMetadataType.DATE]: FieldMetadataDateSettings;
|
[FieldMetadataType.DATE]: FieldMetadataDateSettings;
|
||||||
[FieldMetadataType.DATE_TIME]: FieldMetadataDateTimeSettings;
|
[FieldMetadataType.DATE_TIME]: FieldMetadataDateTimeSettings;
|
||||||
[FieldMetadataType.TEXT]: FieldMetadataTextSettings;
|
[FieldMetadataType.TEXT]: FieldMetadataTextSettings;
|
||||||
|
[FieldMetadataType.RELATION]: FieldMetadataRelationSettings;
|
||||||
};
|
};
|
||||||
|
|
||||||
type SettingsByFieldMetadata<T extends FieldMetadataType | 'default'> =
|
type SettingsByFieldMetadata<T extends FieldMetadataType | 'default'> =
|
||||||
|
|||||||
@ -0,0 +1,6 @@
|
|||||||
|
export enum RelationOnDeleteAction {
|
||||||
|
CASCADE = 'CASCADE',
|
||||||
|
RESTRICT = 'RESTRICT',
|
||||||
|
SET_NULL = 'SET_NULL',
|
||||||
|
NO_ACTION = 'NO_ACTION',
|
||||||
|
}
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
export enum RelationType {
|
||||||
|
ONE_TO_ONE = 'ONE_TO_ONE',
|
||||||
|
ONE_TO_MANY = 'ONE_TO_MANY',
|
||||||
|
MANY_TO_ONE = 'MANY_TO_ONE',
|
||||||
|
}
|
||||||
@ -112,6 +112,12 @@ export class ObjectMetadataEntity implements ObjectMetadataInterface {
|
|||||||
)
|
)
|
||||||
toRelations: Relation<RelationMetadataEntity[]>;
|
toRelations: Relation<RelationMetadataEntity[]>;
|
||||||
|
|
||||||
|
@OneToMany(
|
||||||
|
() => FieldMetadataEntity,
|
||||||
|
(field) => field.relationTargetObjectMetadataId,
|
||||||
|
)
|
||||||
|
targetRelationFields: Relation<FieldMetadataEntity[]>;
|
||||||
|
|
||||||
@ManyToOne(() => DataSourceEntity, (dataSource) => dataSource.objects, {
|
@ManyToOne(() => DataSourceEntity, (dataSource) => dataSource.objects, {
|
||||||
onDelete: 'CASCADE',
|
onDelete: 'CASCADE',
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user