From 04c618284f8fefa4f15cb1197a8d5fabc24c6919 Mon Sep 17 00:00:00 2001 From: Weiko Date: Fri, 10 Nov 2023 15:33:25 +0100 Subject: [PATCH] Convert metadata tables to camel_case (#2420) * Convert metadata tables to camelcase * refactor folder structure * rename datasourcemetadata * regenerate metadata schema * rename dataSourceMetadata to dataSource --- front/src/generated-metadata/graphql.ts | 2 +- .../metadata/hooks/useMetadataField.ts | 2 +- server/ormconfig.ts | 2 +- server/package.json | 2 +- server/src/command.module.ts | 10 +- .../services/workspace.service.spec.ts | 4 +- .../workspace/services/workspace.service.ts | 8 +- server/src/core/workspace/workspace.module.ts | 4 +- .../commands/data-seed-tenant.command.ts | 21 +-- .../commands/database-command.module.ts | 23 ++- server/src/database/seeds/metadata.ts | 4 +- .../typeorm-seeds/metadata/field-metadata.ts | 50 ++--- .../typeorm-seeds/metadata/object-metadata.ts | 2 +- .../typeorm}/metadata/metadata.datasource.ts | 6 +- .../1699619603804-setupMetadataTables.ts | 65 +++++++ server/src/database/typeorm/typeorm.module.ts | 23 +++ .../src/database/typeorm/typeorm.service.ts | 105 +++++++++++ .../commands/metadata-command.module.ts | 31 ---- .../data-source-metadata.module.ts | 12 -- .../data-source-metadata.service.spec.ts | 27 --- .../data-source-metadata.service.ts | 48 ----- .../data-source.entity.ts} | 12 +- .../data-source/data-source.module.ts | 6 +- .../data-source/data-source.service.spec.ts | 34 ---- .../data-source/data-source.service.ts | 171 ++++-------------- .../metadata/data-source/data-source.util.ts | 21 --- .../field-metadata/dtos/create-field.input.ts | 18 +- .../field-metadata/dtos/field-metadata.dto.ts | 81 +++++++++ .../field-metadata.auto-resolver-opts.ts | 44 ----- .../field-metadata/field-metadata.entity.ts | 119 ++++-------- .../field-metadata/field-metadata.module.ts | 44 ++++- .../{services => }/field-metadata.service.ts | 38 ++-- .../hooks/before-create-one-field.hook.ts | 7 +- .../services/field-metadata.service.spec.ts | 43 ----- .../utils/field-metadata.util.ts | 6 +- server/src/metadata/metadata.module.ts | 19 +- .../migration-runner.module.ts | 13 -- .../migration-runner.service.spec.ts | 32 ---- .../1695214465080-InitMetadataTables.ts | 32 ---- ...695717691800-alter-field-metadata-table.ts | 61 ------- .../1696409050890-add-target-column-map.ts | 71 -------- ...7126636202-MetadataNameLabelRefactoring.ts | 149 --------------- ...71445015-removeFieldMetadataPlaceholder.ts | 19 -- .../migrations/1697474804403-addSoftDelete.ts | 23 --- ...moveSingularPluralFromFieldLabelAndName.ts | 49 ----- ...467-addNameAndIsCustomToTenantMigration.ts | 55 ------ ...dUniqueConstraintsOnFieldObjectMetadata.ts | 43 ----- .../1698328717102-removeMetadataSoftDelete.ts | 37 ---- .../1699289664146-addRelationMetadata.ts | 39 ---- .../dtos/create-object.input.ts | 12 +- .../dtos/object-metadata.dto.ts | 64 +++++++ .../hooks/before-create-one-object.hook.ts | 13 +- .../object-metadata.auto-resolver-opts.ts | 44 ----- .../object-metadata/object-metadata.entity.ts | 97 ++++------ .../object-metadata/object-metadata.module.ts | 48 ++++- .../{services => }/object-metadata.service.ts | 63 ++----- .../services/object-metadata.service.spec.ts | 38 ---- .../dtos/create-relation.input.ts | 5 +- .../dtos/relation-metadata.dto.ts | 57 ++++++ .../relation-metadata.auto-resolver-opts.ts | 35 ---- .../relation-metadata.entity.ts | 64 +++---- .../relation-metadata.module.ts | 38 +++- .../relation-metadata.service.ts | 33 ++-- .../tenant-initialisation.service.ts | 90 --------- .../tenant-migration.entity.ts | 4 +- .../tenant-migration.module.ts | 4 +- .../tenant-migration.service.spec.ts | 27 --- .../tenant-migration.service.ts | 12 +- .../tenant-datasource.module.ts | 13 ++ .../tenant-datasource.service.ts | 101 +++++++++++ .../commands/sync-tenant-metadata.command.ts | 16 +- .../tenant-manager-commands.module.ts | 12 ++ .../standard-objects-prefill-data.ts | 0 .../companies/companies.metadata.ts | 0 .../standard-object-metadata.ts | 0 .../view-fields/view-fields.metadata.ts | 0 .../view-filters/view-filters.metadata.ts | 2 +- .../view-sorts/view-sorts.metadata.ts | 2 +- .../standard-objects/views/views.metadata.ts | 2 +- .../tenant-manager.module.ts} | 22 +-- .../tenant-manager/tenant-manager.service.ts | 144 +++++++++++++++ .../commands/run-tenant-migrations.command.ts | 6 +- ...tenant-migration-runner-commands.module.ts | 12 ++ .../tenant-migration-runner.module.ts | 13 ++ .../tenant-migration-runner.service.ts} | 18 +- .../custom-table-default-column.util.ts | 0 .../composite-field-alias.factory.ts | 2 +- .../query-runner/query-runner.module.ts | 4 +- .../query-runner/query-runner.service.ts | 12 +- .../extend-object-type-definition.factory.ts | 2 +- ...ld-metadata-target-column-map.interface.ts | 0 .../interfaces/field-metadata.interface.ts | 10 +- .../money.object-definition.ts | 11 +- .../url.object-definition.ts | 11 +- .../type-definitions.generator.ts | 6 +- server/src/tenant/tenant.module.ts | 4 +- server/src/tenant/tenant.service.spec.ts | 6 +- server/src/tenant/tenant.service.ts | 8 +- 98 files changed, 1189 insertions(+), 1735 deletions(-) rename server/src/{metadata => database}/commands/data-seed-tenant.command.ts (75%) rename server/src/{ => database/typeorm}/metadata/metadata.datasource.ts (92%) create mode 100644 server/src/database/typeorm/metadata/migrations/1699619603804-setupMetadataTables.ts create mode 100644 server/src/database/typeorm/typeorm.module.ts create mode 100644 server/src/database/typeorm/typeorm.service.ts delete mode 100644 server/src/metadata/commands/metadata-command.module.ts delete mode 100644 server/src/metadata/data-source-metadata/data-source-metadata.module.ts delete mode 100644 server/src/metadata/data-source-metadata/data-source-metadata.service.spec.ts delete mode 100644 server/src/metadata/data-source-metadata/data-source-metadata.service.ts rename server/src/metadata/{data-source-metadata/data-source-metadata.entity.ts => data-source/data-source.entity.ts} (69%) delete mode 100644 server/src/metadata/data-source/data-source.service.spec.ts delete mode 100644 server/src/metadata/data-source/data-source.util.ts create mode 100644 server/src/metadata/field-metadata/dtos/field-metadata.dto.ts delete mode 100644 server/src/metadata/field-metadata/field-metadata.auto-resolver-opts.ts rename server/src/metadata/field-metadata/{services => }/field-metadata.service.ts (75%) delete mode 100644 server/src/metadata/field-metadata/services/field-metadata.service.spec.ts delete mode 100644 server/src/metadata/migration-runner/migration-runner.module.ts delete mode 100644 server/src/metadata/migration-runner/migration-runner.service.spec.ts delete mode 100644 server/src/metadata/migrations/1695214465080-InitMetadataTables.ts delete mode 100644 server/src/metadata/migrations/1695717691800-alter-field-metadata-table.ts delete mode 100644 server/src/metadata/migrations/1696409050890-add-target-column-map.ts delete mode 100644 server/src/metadata/migrations/1697126636202-MetadataNameLabelRefactoring.ts delete mode 100644 server/src/metadata/migrations/1697471445015-removeFieldMetadataPlaceholder.ts delete mode 100644 server/src/metadata/migrations/1697474804403-addSoftDelete.ts delete mode 100644 server/src/metadata/migrations/1697534910933-removeSingularPluralFromFieldLabelAndName.ts delete mode 100644 server/src/metadata/migrations/1697622715467-addNameAndIsCustomToTenantMigration.ts delete mode 100644 server/src/metadata/migrations/1697630766924-addUniqueConstraintsOnFieldObjectMetadata.ts delete mode 100644 server/src/metadata/migrations/1698328717102-removeMetadataSoftDelete.ts delete mode 100644 server/src/metadata/migrations/1699289664146-addRelationMetadata.ts create mode 100644 server/src/metadata/object-metadata/dtos/object-metadata.dto.ts delete mode 100644 server/src/metadata/object-metadata/object-metadata.auto-resolver-opts.ts rename server/src/metadata/object-metadata/{services => }/object-metadata.service.ts (68%) delete mode 100644 server/src/metadata/object-metadata/services/object-metadata.service.spec.ts create mode 100644 server/src/metadata/relation-metadata/dtos/relation-metadata.dto.ts delete mode 100644 server/src/metadata/relation-metadata/relation-metadata.auto-resolver-opts.ts rename server/src/metadata/relation-metadata/{services => }/relation-metadata.service.ts (88%) delete mode 100644 server/src/metadata/tenant-initialisation/tenant-initialisation.service.ts delete mode 100644 server/src/metadata/tenant-migration/tenant-migration.service.spec.ts create mode 100644 server/src/tenant-datasource/tenant-datasource.module.ts create mode 100644 server/src/tenant-datasource/tenant-datasource.service.ts rename server/src/{metadata => tenant-manager}/commands/sync-tenant-metadata.command.ts (60%) create mode 100644 server/src/tenant-manager/commands/tenant-manager-commands.module.ts rename server/src/{metadata/tenant-initialisation => tenant-manager}/standard-objects-prefill-data/standard-objects-prefill-data.ts (100%) rename server/src/{metadata => tenant-manager}/standard-objects/companies/companies.metadata.ts (100%) rename server/src/{metadata => tenant-manager}/standard-objects/standard-object-metadata.ts (100%) rename server/src/{metadata => tenant-manager}/standard-objects/view-fields/view-fields.metadata.ts (100%) rename server/src/{metadata => tenant-manager}/standard-objects/view-filters/view-filters.metadata.ts (97%) rename server/src/{metadata => tenant-manager}/standard-objects/view-sorts/view-sorts.metadata.ts (96%) rename server/src/{metadata => tenant-manager}/standard-objects/views/views.metadata.ts (96%) rename server/src/{metadata/tenant-initialisation/tenant-initialisation.module.ts => tenant-manager/tenant-manager.module.ts} (53%) create mode 100644 server/src/tenant-manager/tenant-manager.service.ts rename server/src/{metadata => tenant-migration-runner}/commands/run-tenant-migrations.command.ts (80%) create mode 100644 server/src/tenant-migration-runner/commands/tenant-migration-runner-commands.module.ts create mode 100644 server/src/tenant-migration-runner/tenant-migration-runner.module.ts rename server/src/{metadata/migration-runner/migration-runner.service.ts => tenant-migration-runner/tenant-migration-runner.service.ts} (92%) rename server/src/{metadata/migration-runner => tenant-migration-runner/utils}/custom-table-default-column.util.ts (100%) rename server/src/{metadata/field-metadata => tenant/schema-builder}/interfaces/field-metadata-target-column-map.interface.ts (100%) diff --git a/front/src/generated-metadata/graphql.ts b/front/src/generated-metadata/graphql.ts index 2d2fadd68c8..2b462fd57cf 100644 --- a/front/src/generated-metadata/graphql.ts +++ b/front/src/generated-metadata/graphql.ts @@ -173,7 +173,7 @@ export type CreateFieldInput = { icon?: InputMaybe; label: Scalars['String']['input']; name: Scalars['String']['input']; - objectId: Scalars['String']['input']; + objectMetadataId: Scalars['String']['input']; type: FieldMetadataType; }; diff --git a/front/src/modules/metadata/hooks/useMetadataField.ts b/front/src/modules/metadata/hooks/useMetadataField.ts index 938fafdabe1..a72bf40a36a 100644 --- a/front/src/modules/metadata/hooks/useMetadataField.ts +++ b/front/src/modules/metadata/hooks/useMetadataField.ts @@ -20,7 +20,7 @@ export const useMetadataField = () => { ) => createOneMetadataField({ ...formatMetadataFieldInput(input), - objectId: input.objectMetadataId, + objectMetadataId: input.objectMetadataId, type: input.type, }); diff --git a/server/ormconfig.ts b/server/ormconfig.ts index 19a3d8f28be..153603fdc64 100644 --- a/server/ormconfig.ts +++ b/server/ormconfig.ts @@ -7,7 +7,7 @@ export default { type: 'postgres', entities: [__dirname + '/src/coreV2/**/*.entity{.ts,.js}'], synchronize: false, - migrationsRun: true, + migrationsRun: false, migrationsTableName: '_typeorm_migrations', migrations: [__dirname + '/migrations/**/*{.ts,.js}'], cli: { diff --git a/server/package.json b/server/package.json index 76a88ff9bc1..703a33ba1b2 100644 --- a/server/package.json +++ b/server/package.json @@ -27,7 +27,7 @@ "prisma:seed": "npx prisma db seed", "prisma:migrate": "npx prisma migrate deploy", "typeorm": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js", - "typeorm:migrate": "yarn typeorm migration:run -d ./src/metadata/metadata.datasource.ts", + "typeorm:migrate": "yarn typeorm migration:run -d ./src/database/typeorm/metadata/metadata.datasource.ts", "database:init": "yarn database:setup && yarn database:seed", "database:setup": "npx ts-node ./scripts/setup-db.ts && yarn database:migrate && yarn database:generate", "database:truncate": "npx ts-node ./scripts/truncate-db.ts", diff --git a/server/src/command.module.ts b/server/src/command.module.ts index a875dbd6ae1..f567b4c6e5f 100644 --- a/server/src/command.module.ts +++ b/server/src/command.module.ts @@ -4,9 +4,15 @@ import { DatabaseCommandModule } from 'src/database/commands/database-command.mo import { AppModule } from './app.module'; -import { MetadataCommandModule } from './metadata/commands/metadata-command.module'; +import { TenantManagerCommandsModule } from './tenant-manager/commands/tenant-manager-commands.module'; +import { TenantMigrationRunnerCommandsModule } from './tenant-migration-runner/commands/tenant-migration-runner-commands.module'; @Module({ - imports: [AppModule, MetadataCommandModule, DatabaseCommandModule], + imports: [ + AppModule, + TenantMigrationRunnerCommandsModule, + TenantManagerCommandsModule, + DatabaseCommandModule, + ], }) export class CommandModule {} diff --git a/server/src/core/workspace/services/workspace.service.spec.ts b/server/src/core/workspace/services/workspace.service.spec.ts index 3a73c544e03..6e3e9f80a7c 100644 --- a/server/src/core/workspace/services/workspace.service.spec.ts +++ b/server/src/core/workspace/services/workspace.service.spec.ts @@ -7,7 +7,7 @@ import { PipelineStageService } from 'src/core/pipeline/services/pipeline-stage. import { PersonService } from 'src/core/person/person.service'; import { CompanyService } from 'src/core/company/company.service'; import { PipelineProgressService } from 'src/core/pipeline/services/pipeline-progress.service'; -import { TenantInitialisationService } from 'src/metadata/tenant-initialisation/tenant-initialisation.service'; +import { TenantManagerService } from 'src/tenant-manager/tenant-manager.service'; import { WorkspaceService } from './workspace.service'; @@ -43,7 +43,7 @@ describe('WorkspaceService', () => { useValue: {}, }, { - provide: TenantInitialisationService, + provide: TenantManagerService, useValue: {}, }, ], diff --git a/server/src/core/workspace/services/workspace.service.ts b/server/src/core/workspace/services/workspace.service.ts index 17c2fb7c3dc..075c2d96bdf 100644 --- a/server/src/core/workspace/services/workspace.service.ts +++ b/server/src/core/workspace/services/workspace.service.ts @@ -10,7 +10,7 @@ import { PipelineStageService } from 'src/core/pipeline/services/pipeline-stage. import { PipelineService } from 'src/core/pipeline/services/pipeline.service'; import { PrismaService } from 'src/database/prisma.service'; import { assert } from 'src/utils/assert'; -import { TenantInitialisationService } from 'src/metadata/tenant-initialisation/tenant-initialisation.service'; +import { TenantManagerService } from 'src/tenant-manager/tenant-manager.service'; @Injectable() export class WorkspaceService { @@ -21,7 +21,7 @@ export class WorkspaceService { private readonly personService: PersonService, private readonly pipelineStageService: PipelineStageService, private readonly pipelineProgressService: PipelineProgressService, - private readonly tenantInitialisationService: TenantInitialisationService, + private readonly tenantManagerService: TenantManagerService, ) {} // Find @@ -64,7 +64,7 @@ export class WorkspaceService { }); // Create workspace schema - await this.tenantInitialisationService.init(workspace.id); + await this.tenantManagerService.init(workspace.id); // Create default companies const companies = await this.companyService.createDefaultCompanies({ @@ -161,7 +161,7 @@ export class WorkspaceService { this.delete({ where: { id: workspaceId } }), ]); - await this.tenantInitialisationService.delete(workspaceId); + await this.tenantManagerService.delete(workspaceId); return workspace; } diff --git a/server/src/core/workspace/workspace.module.ts b/server/src/core/workspace/workspace.module.ts index be1e24c0371..1bcbd1a90bc 100644 --- a/server/src/core/workspace/workspace.module.ts +++ b/server/src/core/workspace/workspace.module.ts @@ -4,7 +4,7 @@ import { FileUploadService } from 'src/core/file/services/file-upload.service'; import { PipelineModule } from 'src/core/pipeline/pipeline.module'; import { CompanyModule } from 'src/core/company/company.module'; import { PersonModule } from 'src/core/person/person.module'; -import { TenantInitialisationModule } from 'src/metadata/tenant-initialisation/tenant-initialisation.module'; +import { TenantManagerModule } from 'src/tenant-manager/tenant-manager.module'; import { AbilityModule } from 'src/ability/ability.module'; import { PrismaModule } from 'src/database/prisma.module'; @@ -19,7 +19,7 @@ import { WorkspaceResolver } from './resolvers/workspace.resolver'; PipelineModule, CompanyModule, PersonModule, - TenantInitialisationModule, + TenantManagerModule, PrismaModule, ], providers: [ diff --git a/server/src/metadata/commands/data-seed-tenant.command.ts b/server/src/database/commands/data-seed-tenant.command.ts similarity index 75% rename from server/src/metadata/commands/data-seed-tenant.command.ts rename to server/src/database/commands/data-seed-tenant.command.ts index 107aa917dc9..4b5d6314d6c 100644 --- a/server/src/metadata/commands/data-seed-tenant.command.ts +++ b/server/src/database/commands/data-seed-tenant.command.ts @@ -3,15 +3,15 @@ import { InjectDataSource } from '@nestjs/typeorm'; import { Command, CommandRunner } from 'nest-commander'; import { DataSource } from 'typeorm'; -import { DataSourceMetadataService } from 'src/metadata/data-source-metadata/data-source-metadata.service'; import { DataSourceService } from 'src/metadata/data-source/data-source.service'; import { TenantMigrationService } from 'src/metadata/tenant-migration/tenant-migration.service'; -import { MigrationRunnerService } from 'src/metadata/migration-runner/migration-runner.service'; +import { TenantMigrationRunnerService } from 'src/tenant-migration-runner/tenant-migration-runner.service'; import { seedCompanies } from 'src/database/typeorm-seeds/tenant/companies'; import { seedViewFields } from 'src/database/typeorm-seeds/tenant/view-fields'; import { seedViews } from 'src/database/typeorm-seeds/tenant/views'; import { seedFieldMetadata } from 'src/database/typeorm-seeds/metadata/field-metadata'; import { seedObjectMetadata } from 'src/database/typeorm-seeds/metadata/object-metadata'; +import { TypeORMService } from 'src/database/typeorm/typeorm.service'; // TODO: implement dry-run @Command({ @@ -25,24 +25,23 @@ export class DataSeedTenantCommand extends CommandRunner { constructor( @InjectDataSource('metadata') private readonly metadataDataSource: DataSource, - private readonly dataSourceMetadataService: DataSourceMetadataService, private readonly dataSourceService: DataSourceService, + private readonly typeORMService: TypeORMService, private readonly tenantMigrationService: TenantMigrationService, - private readonly migrationRunnerService: MigrationRunnerService, + private readonly migrationRunnerService: TenantMigrationRunnerService, ) { super(); } async run(): Promise { const dataSourceMetadata = - await this.dataSourceMetadataService.getLastDataSourceMetadataFromWorkspaceIdOrFail( + await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail( this.workspaceId, ); - const workspaceDataSource = - await this.dataSourceService.connectToWorkspaceDataSource( - this.workspaceId, - ); + const workspaceDataSource = await this.typeORMService.connectToDataSource( + dataSourceMetadata, + ); if (!workspaceDataSource) { throw new Error('Could not connect to workspace data source'); @@ -62,8 +61,6 @@ export class DataSeedTenantCommand extends CommandRunner { await seedViewFields(workspaceDataSource, dataSourceMetadata.schema); await seedViews(workspaceDataSource, dataSourceMetadata.schema); - await this.dataSourceService.disconnectFromWorkspaceDataSource( - this.workspaceId, - ); + await this.typeORMService.disconnectFromDataSource(dataSourceMetadata.id); } } diff --git a/server/src/database/commands/database-command.module.ts b/server/src/database/commands/database-command.module.ts index 3d89ee29ec3..5057754ba37 100644 --- a/server/src/database/commands/database-command.module.ts +++ b/server/src/database/commands/database-command.module.ts @@ -2,21 +2,36 @@ import { Module } from '@nestjs/common'; import { DataCleanInactiveCommand } from 'src/database/commands/clean-inactive-workspaces.command'; import { ConfirmationQuestion } from 'src/database/commands/questions/confirmation.question'; -import { WorkspaceService } from 'src/core/workspace/services/workspace.service'; import { PipelineModule } from 'src/core/pipeline/pipeline.module'; import { CompanyModule } from 'src/core/company/company.module'; import { PersonModule } from 'src/core/person/person.module'; -import { TenantInitialisationModule } from 'src/metadata/tenant-initialisation/tenant-initialisation.module'; import { PrismaModule } from 'src/database/prisma.module'; +import { TenantManagerModule } from 'src/tenant-manager/tenant-manager.module'; +import { DataSourceModule } from 'src/metadata/data-source/data-source.module'; +import { TenantMigrationModule } from 'src/metadata/tenant-migration/tenant-migration.module'; +import { TenantMigrationRunnerModule } from 'src/tenant-migration-runner/tenant-migration-runner.module'; +import { TypeORMModule } from 'src/database/typeorm/typeorm.module'; +import { WorkspaceModule } from 'src/core/workspace/workspace.module'; + +import { DataSeedTenantCommand } from './data-seed-tenant.command'; @Module({ imports: [ PipelineModule, CompanyModule, PersonModule, - TenantInitialisationModule, + TenantManagerModule, PrismaModule, + DataSourceModule, + TypeORMModule, + TenantMigrationModule, + TenantMigrationRunnerModule, + WorkspaceModule, + ], + providers: [ + DataSeedTenantCommand, + DataCleanInactiveCommand, + ConfirmationQuestion, ], - providers: [DataCleanInactiveCommand, ConfirmationQuestion, WorkspaceService], }) export class DatabaseCommandModule {} diff --git a/server/src/database/seeds/metadata.ts b/server/src/database/seeds/metadata.ts index 575ace3b0cf..bf62ea6b057 100644 --- a/server/src/database/seeds/metadata.ts +++ b/server/src/database/seeds/metadata.ts @@ -5,8 +5,8 @@ export const seedMetadata = async (prisma: PrismaClient) => { 'CREATE SCHEMA IF NOT EXISTS workspace_twenty_7icsva0r6s00mpcp6cwg4w4rd', ); await prisma.$queryRawUnsafe( - `INSERT INTO metadata.data_source_metadata( - id, schema, type, workspace_id + `INSERT INTO metadata."dataSource"( + id, schema, type, "workspaceId" ) VALUES ( 'b37b2163-7f63-47a9-b1b3-6c7290ca9fb1', 'workspace_twenty_7icsva0r6s00mpcp6cwg4w4rd', 'postgres', 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419' diff --git a/server/src/database/typeorm-seeds/metadata/field-metadata.ts b/server/src/database/typeorm-seeds/metadata/field-metadata.ts index 6c097eb9522..1900f52609c 100644 --- a/server/src/database/typeorm-seeds/metadata/field-metadata.ts +++ b/server/src/database/typeorm-seeds/metadata/field-metadata.ts @@ -1,6 +1,6 @@ import { DataSource } from 'typeorm'; -const tableName = 'field_metadata'; +const tableName = 'fieldMetadata'; export const seedFieldMetadata = async ( workspaceDataSource: DataSource, @@ -10,7 +10,7 @@ export const seedFieldMetadata = async ( .createQueryBuilder() .insert() .into(`${schemaName}.${tableName}`, [ - 'objectId', + 'objectMetadataId', 'isCustom', 'workspaceId', 'isActive', @@ -26,7 +26,7 @@ export const seedFieldMetadata = async ( .values([ // Companies { - objectId: '1a8487a0-480c-434e-b4c7-e22408b97047', + objectMetadataId: '1a8487a0-480c-434e-b4c7-e22408b97047', isCustom: false, workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', isActive: true, @@ -41,7 +41,7 @@ export const seedFieldMetadata = async ( isNullable: false, }, { - objectId: '1a8487a0-480c-434e-b4c7-e22408b97047', + objectMetadataId: '1a8487a0-480c-434e-b4c7-e22408b97047', isCustom: false, workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', isActive: true, @@ -56,7 +56,7 @@ export const seedFieldMetadata = async ( isNullable: true, }, { - objectId: '1a8487a0-480c-434e-b4c7-e22408b97047', + objectMetadataId: '1a8487a0-480c-434e-b4c7-e22408b97047', isCustom: false, workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', isActive: true, @@ -71,7 +71,7 @@ export const seedFieldMetadata = async ( isNullable: true, }, { - objectId: '1a8487a0-480c-434e-b4c7-e22408b97047', + objectMetadataId: '1a8487a0-480c-434e-b4c7-e22408b97047', isCustom: false, workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', isActive: true, @@ -87,7 +87,7 @@ export const seedFieldMetadata = async ( }, // Views { - objectId: '9ab6b3dc-767f-473f-8fd0-6cdbefbf8dbe', + objectMetadataId: '9ab6b3dc-767f-473f-8fd0-6cdbefbf8dbe', isCustom: false, workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', isActive: true, @@ -102,13 +102,13 @@ export const seedFieldMetadata = async ( isNullable: false, }, { - objectId: '9ab6b3dc-767f-473f-8fd0-6cdbefbf8dbe', + objectMetadataId: '9ab6b3dc-767f-473f-8fd0-6cdbefbf8dbe', isCustom: false, workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', isActive: true, type: 'TEXT', name: 'objectMetadataId', - label: 'Object Id', + label: 'Object Metadata Id', targetColumnMap: { value: 'objectMetadataId', }, @@ -117,7 +117,7 @@ export const seedFieldMetadata = async ( isNullable: false, }, { - objectId: '9ab6b3dc-767f-473f-8fd0-6cdbefbf8dbe', + objectMetadataId: '9ab6b3dc-767f-473f-8fd0-6cdbefbf8dbe', isCustom: false, workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', isActive: true, @@ -133,13 +133,13 @@ export const seedFieldMetadata = async ( }, // View Fields { - objectId: '61d9000b-485c-4c48-a22e-0d9a164f9647', + objectMetadataId: '61d9000b-485c-4c48-a22e-0d9a164f9647', isCustom: false, workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', isActive: true, type: 'TEXT', name: 'fieldMetadataId', - label: 'Field Id', + label: 'Field Metadata Id', targetColumnMap: { value: 'fieldMetadataId', }, @@ -148,7 +148,7 @@ export const seedFieldMetadata = async ( isNullable: false, }, { - objectId: '61d9000b-485c-4c48-a22e-0d9a164f9647', + objectMetadataId: '61d9000b-485c-4c48-a22e-0d9a164f9647', isCustom: false, workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', isActive: true, @@ -163,7 +163,7 @@ export const seedFieldMetadata = async ( isNullable: false, }, { - objectId: '61d9000b-485c-4c48-a22e-0d9a164f9647', + objectMetadataId: '61d9000b-485c-4c48-a22e-0d9a164f9647', isCustom: false, workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', isActive: true, @@ -178,7 +178,7 @@ export const seedFieldMetadata = async ( isNullable: false, }, { - objectId: '61d9000b-485c-4c48-a22e-0d9a164f9647', + objectMetadataId: '61d9000b-485c-4c48-a22e-0d9a164f9647', isCustom: false, workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', isActive: true, @@ -193,7 +193,7 @@ export const seedFieldMetadata = async ( isNullable: false, }, { - objectId: '61d9000b-485c-4c48-a22e-0d9a164f9647', + objectMetadataId: '61d9000b-485c-4c48-a22e-0d9a164f9647', isCustom: false, workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', isActive: true, @@ -209,13 +209,13 @@ export const seedFieldMetadata = async ( }, // View Filters { - objectId: '5d9b1ab9-4461-4e2d-bf9e-9b47e68846d3', + objectMetadataId: '5d9b1ab9-4461-4e2d-bf9e-9b47e68846d3', isCustom: false, workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', isActive: true, type: 'TEXT', name: 'fieldMetadataId', - label: 'Field Id', + label: 'Field Metadata Id', targetColumnMap: { value: 'fieldMetadataId', }, @@ -224,7 +224,7 @@ export const seedFieldMetadata = async ( isNullable: false, }, { - objectId: '5d9b1ab9-4461-4e2d-bf9e-9b47e68846d3', + objectMetadataId: '5d9b1ab9-4461-4e2d-bf9e-9b47e68846d3', isCustom: false, workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', isActive: true, @@ -239,7 +239,7 @@ export const seedFieldMetadata = async ( isNullable: false, }, { - objectId: '5d9b1ab9-4461-4e2d-bf9e-9b47e68846d3', + objectMetadataId: '5d9b1ab9-4461-4e2d-bf9e-9b47e68846d3', isCustom: false, workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', isActive: true, @@ -254,7 +254,7 @@ export const seedFieldMetadata = async ( isNullable: false, }, { - objectId: '5d9b1ab9-4461-4e2d-bf9e-9b47e68846d3', + objectMetadataId: '5d9b1ab9-4461-4e2d-bf9e-9b47e68846d3', isCustom: false, workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', isActive: true, @@ -269,7 +269,7 @@ export const seedFieldMetadata = async ( isNullable: false, }, { - objectId: '5d9b1ab9-4461-4e2d-bf9e-9b47e68846d3', + objectMetadataId: '5d9b1ab9-4461-4e2d-bf9e-9b47e68846d3', isCustom: false, workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', isActive: true, @@ -285,7 +285,7 @@ export const seedFieldMetadata = async ( }, // View Sorts { - objectId: '6f8dcd4b-cf28-41dd-b98b-d6e1f5b3a251', + objectMetadataId: '6f8dcd4b-cf28-41dd-b98b-d6e1f5b3a251', isCustom: false, workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', isActive: true, @@ -300,7 +300,7 @@ export const seedFieldMetadata = async ( isNullable: false, }, { - objectId: '6f8dcd4b-cf28-41dd-b98b-d6e1f5b3a251', + objectMetadataId: '6f8dcd4b-cf28-41dd-b98b-d6e1f5b3a251', isCustom: false, workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', isActive: true, @@ -315,7 +315,7 @@ export const seedFieldMetadata = async ( isNullable: false, }, { - objectId: '6f8dcd4b-cf28-41dd-b98b-d6e1f5b3a251', + objectMetadataId: '6f8dcd4b-cf28-41dd-b98b-d6e1f5b3a251', isCustom: false, workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', isActive: true, diff --git a/server/src/database/typeorm-seeds/metadata/object-metadata.ts b/server/src/database/typeorm-seeds/metadata/object-metadata.ts index 25131b86c95..5f96fced6d0 100644 --- a/server/src/database/typeorm-seeds/metadata/object-metadata.ts +++ b/server/src/database/typeorm-seeds/metadata/object-metadata.ts @@ -1,6 +1,6 @@ import { DataSource } from 'typeorm'; -const tableName = 'object_metadata'; +const tableName = 'objectMetadata'; export const seedObjectMetadata = async ( workspaceDataSource: DataSource, diff --git a/server/src/metadata/metadata.datasource.ts b/server/src/database/typeorm/metadata/metadata.datasource.ts similarity index 92% rename from server/src/metadata/metadata.datasource.ts rename to server/src/database/typeorm/metadata/metadata.datasource.ts index 4671d174a03..9be4ff2db45 100644 --- a/server/src/metadata/metadata.datasource.ts +++ b/server/src/database/typeorm/metadata/metadata.datasource.ts @@ -3,23 +3,19 @@ import { TypeOrmModuleOptions } from '@nestjs/typeorm'; import { DataSource, DataSourceOptions } from 'typeorm'; import { config } from 'dotenv'; - config(); - const configService = new ConfigService(); - export const typeORMMetadataModuleOptions: TypeOrmModuleOptions = { url: configService.get('PG_DATABASE_URL'), type: 'postgres', logging: ['error'], schema: 'metadata', - entities: [__dirname + '/**/*.entity{.ts,.js}'], + entities: ['dist/src/metadata/**/*.entity{.ts,.js}'], synchronize: false, migrationsRun: false, migrationsTableName: '_typeorm_migrations', migrations: [__dirname + '/migrations/*{.ts,.js}'], }; - export const connectionSource = new DataSource( typeORMMetadataModuleOptions as DataSourceOptions, ); diff --git a/server/src/database/typeorm/metadata/migrations/1699619603804-setupMetadataTables.ts b/server/src/database/typeorm/metadata/migrations/1699619603804-setupMetadataTables.ts new file mode 100644 index 00000000000..f5e3d10cda3 --- /dev/null +++ b/server/src/database/typeorm/metadata/migrations/1699619603804-setupMetadataTables.ts @@ -0,0 +1,65 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class SetupMetadataTables1699619603804 implements MigrationInterface { + name = 'SetupMetadataTables1699619603804'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TYPE "metadata"."dataSource_type_enum" AS ENUM('postgres')`, + ); + await queryRunner.query( + `CREATE TABLE "metadata"."dataSource" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "url" character varying, "schema" character varying, "type" "metadata"."dataSource_type_enum" NOT NULL DEFAULT 'postgres', "label" character varying, "isRemote" boolean NOT NULL DEFAULT false, "workspaceId" character varying NOT NULL, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "PK_6d01ae6c0f47baf4f8e37342268" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE TABLE "metadata"."relationMetadata" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "relationType" character varying NOT NULL, "fromObjectMetadataId" uuid NOT NULL, "toObjectMetadataId" uuid NOT NULL, "fromFieldMetadataId" uuid NOT NULL, "toFieldMetadataId" uuid NOT NULL, "workspaceId" character varying NOT NULL, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "REL_3deb257254145a3bdde9575e7d" UNIQUE ("fromFieldMetadataId"), CONSTRAINT "REL_9dea8f90d04edbbf9c541a95c3" UNIQUE ("toFieldMetadataId"), CONSTRAINT "PK_2724f60cb4f17a89481a7e8d7d3" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE TABLE "metadata"."fieldMetadata" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "objectMetadataId" uuid NOT NULL, "type" character varying NOT NULL, "name" character varying NOT NULL, "label" character varying NOT NULL, "targetColumnMap" jsonb NOT NULL, "description" text, "icon" character varying, "enums" text array, "isCustom" boolean NOT NULL DEFAULT false, "isActive" boolean NOT NULL DEFAULT false, "isNullable" boolean DEFAULT true, "workspaceId" character varying NOT NULL, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "IndexOnNameObjectMetadataIdAndWorkspaceIdUnique" UNIQUE ("name", "objectMetadataId", "workspaceId"), CONSTRAINT "PK_d046b1c7cea325ebc4cdc25e7a9" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE TABLE "metadata"."objectMetadata" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "dataSourceId" character varying NOT NULL, "nameSingular" character varying NOT NULL, "namePlural" character varying NOT NULL, "labelSingular" character varying NOT NULL, "labelPlural" character varying NOT NULL, "description" text, "icon" character varying, "targetTableName" character varying NOT NULL, "isCustom" boolean NOT NULL DEFAULT false, "isActive" boolean NOT NULL DEFAULT false, "workspaceId" character varying NOT NULL, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "IndexOnNamePluralAndWorkspaceIdUnique" UNIQUE ("namePlural", "workspaceId"), CONSTRAINT "IndexOnNameSingularAndWorkspaceIdUnique" UNIQUE ("nameSingular", "workspaceId"), CONSTRAINT "PK_81fb7f4f4244211cfbd188af1e8" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE TABLE "metadata"."tenantMigration" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "migrations" jsonb, "name" character varying, "isCustom" boolean NOT NULL DEFAULT false, "appliedAt" TIMESTAMP, "workspaceId" character varying NOT NULL, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "PK_f9b06eb42494795f73acb5c2350" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `ALTER TABLE "metadata"."relationMetadata" ADD CONSTRAINT "FK_f2a0acd3a548ee446a1a35df44d" FOREIGN KEY ("fromObjectMetadataId") REFERENCES "metadata"."objectMetadata"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "metadata"."relationMetadata" ADD CONSTRAINT "FK_0f781f589e5a527b8f3d3a4b824" FOREIGN KEY ("toObjectMetadataId") REFERENCES "metadata"."objectMetadata"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "metadata"."relationMetadata" ADD CONSTRAINT "FK_3deb257254145a3bdde9575e7d6" FOREIGN KEY ("fromFieldMetadataId") REFERENCES "metadata"."fieldMetadata"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "metadata"."relationMetadata" ADD CONSTRAINT "FK_9dea8f90d04edbbf9c541a95c3b" FOREIGN KEY ("toFieldMetadataId") REFERENCES "metadata"."fieldMetadata"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "metadata"."fieldMetadata" ADD CONSTRAINT "FK_de2a09b9e3e690440480d2dee26" FOREIGN KEY ("objectMetadataId") REFERENCES "metadata"."objectMetadata"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "metadata"."fieldMetadata" DROP CONSTRAINT "FK_de2a09b9e3e690440480d2dee26"`, + ); + await queryRunner.query( + `ALTER TABLE "metadata"."relationMetadata" DROP CONSTRAINT "FK_9dea8f90d04edbbf9c541a95c3b"`, + ); + await queryRunner.query( + `ALTER TABLE "metadata"."relationMetadata" DROP CONSTRAINT "FK_3deb257254145a3bdde9575e7d6"`, + ); + await queryRunner.query( + `ALTER TABLE "metadata"."relationMetadata" DROP CONSTRAINT "FK_0f781f589e5a527b8f3d3a4b824"`, + ); + await queryRunner.query( + `ALTER TABLE "metadata"."relationMetadata" DROP CONSTRAINT "FK_f2a0acd3a548ee446a1a35df44d"`, + ); + await queryRunner.query(`DROP TABLE "metadata"."tenantMigration"`); + await queryRunner.query(`DROP TABLE "metadata"."objectMetadata"`); + await queryRunner.query(`DROP TABLE "metadata"."fieldMetadata"`); + await queryRunner.query(`DROP TABLE "metadata"."relationMetadata"`); + await queryRunner.query(`DROP TABLE "metadata"."dataSource"`); + await queryRunner.query(`DROP TYPE "metadata"."dataSource_type_enum"`); + } +} diff --git a/server/src/database/typeorm/typeorm.module.ts b/server/src/database/typeorm/typeorm.module.ts new file mode 100644 index 00000000000..955f44f8f8f --- /dev/null +++ b/server/src/database/typeorm/typeorm.module.ts @@ -0,0 +1,23 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule, TypeOrmModuleOptions } from '@nestjs/typeorm'; + +import { TypeORMService } from './typeorm.service'; + +import { typeORMMetadataModuleOptions } from './metadata/metadata.datasource'; + +const metadataTypeORMFactory = async (): Promise => ({ + ...typeORMMetadataModuleOptions, + name: 'metadata', +}); + +@Module({ + imports: [ + TypeOrmModule.forRootAsync({ + useFactory: metadataTypeORMFactory, + name: 'metadata', + }), + ], + providers: [TypeORMService], + exports: [TypeORMService], +}) +export class TypeORMModule {} diff --git a/server/src/database/typeorm/typeorm.service.ts b/server/src/database/typeorm/typeorm.service.ts new file mode 100644 index 00000000000..5159d8d9055 --- /dev/null +++ b/server/src/database/typeorm/typeorm.service.ts @@ -0,0 +1,105 @@ +import { Injectable, OnModuleDestroy, OnModuleInit } from '@nestjs/common'; + +import { DataSource } from 'typeorm'; + +import { EnvironmentService } from 'src/integrations/environment/environment.service'; +import { DataSourceEntity } from 'src/metadata/data-source/data-source.entity'; + +@Injectable() +export class TypeORMService implements OnModuleInit, OnModuleDestroy { + private mainDataSource: DataSource; + private dataSources: Map = new Map(); + + constructor(private readonly environmentService: EnvironmentService) { + this.mainDataSource = new DataSource({ + url: environmentService.getPGDatabaseUrl(), + type: 'postgres', + logging: false, + schema: 'public', + }); + } + + /** + * Connects to a data source using metadata. Returns a cached connection if it exists. + * @param dataSource DataSourceEntity + * @returns Promise + */ + public async connectToDataSource( + dataSource: DataSourceEntity, + ): Promise { + if (this.dataSources.has(dataSource.id)) { + return this.dataSources.get(dataSource.id); + } + + const schema = dataSource.schema; + + const workspaceDataSource = new DataSource({ + url: dataSource.url ?? this.environmentService.getPGDatabaseUrl(), + type: 'postgres', + logging: ['query'], + schema, + }); + + await workspaceDataSource.initialize(); + + this.dataSources.set(dataSource.id, workspaceDataSource); + + return workspaceDataSource; + } + + /** + * Disconnects from a workspace data source. + * @param dataSourceId + * @returns Promise + * + */ + public async disconnectFromDataSource(dataSourceId: string) { + if (!this.dataSources.has(dataSourceId)) { + return; + } + + const dataSource = this.dataSources.get(dataSourceId); + + await dataSource?.destroy(); + + this.dataSources.delete(dataSourceId); + } + + /** + * Creates a new schema + * @param workspaceId + * @returns Promise + */ + public async createSchema(schemaName: string): Promise { + const queryRunner = this.mainDataSource.createQueryRunner(); + + await queryRunner.createSchema(schemaName, true); + + await queryRunner.release(); + + return schemaName; + } + + public async deleteSchema(schemaName: string) { + const queryRunner = this.mainDataSource.createQueryRunner(); + + await queryRunner.dropSchema(schemaName, true, true); + + await queryRunner.release(); + } + + async onModuleInit() { + // Init main data source "default" schema + await this.mainDataSource.initialize(); + } + + async onModuleDestroy() { + // Destroy main data source "default" schema + await this.mainDataSource.destroy(); + + // Destroy all workspace data sources + for (const [, dataSource] of this.dataSources) { + await dataSource.destroy(); + } + } +} diff --git a/server/src/metadata/commands/metadata-command.module.ts b/server/src/metadata/commands/metadata-command.module.ts deleted file mode 100644 index 14f0919c22e..00000000000 --- a/server/src/metadata/commands/metadata-command.module.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Module } from '@nestjs/common'; - -import { TenantMigrationModule } from 'src/metadata/tenant-migration/tenant-migration.module'; -import { MigrationRunnerModule } from 'src/metadata/migration-runner/migration-runner.module'; -import { ObjectMetadataModule } from 'src/metadata/object-metadata/object-metadata.module'; -import { FieldMetadataModule } from 'src/metadata/field-metadata/field-metadata.module'; -import { TenantInitialisationModule } from 'src/metadata/tenant-initialisation/tenant-initialisation.module'; -import { DataSourceMetadataModule } from 'src/metadata/data-source-metadata/data-source-metadata.module'; -import { DataSourceModule } from 'src/metadata/data-source/data-source.module'; - -import { SyncTenantMetadataCommand } from './sync-tenant-metadata.command'; -import { RunTenantMigrationsCommand } from './run-tenant-migrations.command'; -import { DataSeedTenantCommand } from './data-seed-tenant.command'; - -@Module({ - imports: [ - TenantMigrationModule, - MigrationRunnerModule, - ObjectMetadataModule, - FieldMetadataModule, - DataSourceMetadataModule, - TenantInitialisationModule, - DataSourceModule, - ], - providers: [ - RunTenantMigrationsCommand, - SyncTenantMetadataCommand, - DataSeedTenantCommand, - ], -}) -export class MetadataCommandModule {} diff --git a/server/src/metadata/data-source-metadata/data-source-metadata.module.ts b/server/src/metadata/data-source-metadata/data-source-metadata.module.ts deleted file mode 100644 index b4fdc2f38d9..00000000000 --- a/server/src/metadata/data-source-metadata/data-source-metadata.module.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Module } from '@nestjs/common'; -import { TypeOrmModule } from '@nestjs/typeorm'; - -import { DataSourceMetadataService } from './data-source-metadata.service'; -import { DataSourceMetadata } from './data-source-metadata.entity'; - -@Module({ - imports: [TypeOrmModule.forFeature([DataSourceMetadata], 'metadata')], - providers: [DataSourceMetadataService], - exports: [DataSourceMetadataService], -}) -export class DataSourceMetadataModule {} diff --git a/server/src/metadata/data-source-metadata/data-source-metadata.service.spec.ts b/server/src/metadata/data-source-metadata/data-source-metadata.service.spec.ts deleted file mode 100644 index 50403ed8b85..00000000000 --- a/server/src/metadata/data-source-metadata/data-source-metadata.service.spec.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { getRepositoryToken } from '@nestjs/typeorm'; - -import { DataSourceMetadataService } from './data-source-metadata.service'; -import { DataSourceMetadata } from './data-source-metadata.entity'; - -describe('DataSourceMetadataService', () => { - let service: DataSourceMetadataService; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - DataSourceMetadataService, - { - provide: getRepositoryToken(DataSourceMetadata, 'metadata'), - useValue: {}, - }, - ], - }).compile(); - - service = module.get(DataSourceMetadataService); - }); - - it('should be defined', () => { - expect(service).toBeDefined(); - }); -}); diff --git a/server/src/metadata/data-source-metadata/data-source-metadata.service.ts b/server/src/metadata/data-source-metadata/data-source-metadata.service.ts deleted file mode 100644 index 6698a2bf632..00000000000 --- a/server/src/metadata/data-source-metadata/data-source-metadata.service.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; - -import { Repository } from 'typeorm'; - -import { DataSourceMetadata } from './data-source-metadata.entity'; - -@Injectable() -export class DataSourceMetadataService { - constructor( - @InjectRepository(DataSourceMetadata, 'metadata') - private readonly dataSourceMetadataRepository: Repository, - ) {} - - async createDataSourceMetadata(workspaceId: string, workspaceSchema: string) { - // TODO: Double check if this is the correct way to do this - const dataSource = await this.dataSourceMetadataRepository.findOne({ - where: { workspaceId }, - }); - - if (dataSource) { - return dataSource; - } - - return this.dataSourceMetadataRepository.save({ - workspaceId, - schema: workspaceSchema, - }); - } - - async getDataSourcesMetadataFromWorkspaceId(workspaceId: string) { - return this.dataSourceMetadataRepository.find({ - where: { workspaceId }, - order: { createdAt: 'DESC' }, - }); - } - - async getLastDataSourceMetadataFromWorkspaceIdOrFail(workspaceId: string) { - return this.dataSourceMetadataRepository.findOneOrFail({ - where: { workspaceId }, - order: { createdAt: 'DESC' }, - }); - } - - async delete(workspaceId: string) { - await this.dataSourceMetadataRepository.delete({ workspaceId }); - } -} diff --git a/server/src/metadata/data-source-metadata/data-source-metadata.entity.ts b/server/src/metadata/data-source/data-source.entity.ts similarity index 69% rename from server/src/metadata/data-source-metadata/data-source-metadata.entity.ts rename to server/src/metadata/data-source/data-source.entity.ts index 0af0aa0ed5a..d39ac33f270 100644 --- a/server/src/metadata/data-source-metadata/data-source-metadata.entity.ts +++ b/server/src/metadata/data-source/data-source.entity.ts @@ -9,8 +9,8 @@ import { type DataSourceType = DataSourceOptions['type']; -@Entity('data_source_metadata') -export class DataSourceMetadata { +@Entity('dataSource') +export class DataSourceEntity { @PrimaryGeneratedColumn('uuid') id: string; @@ -26,15 +26,15 @@ export class DataSourceMetadata { @Column({ nullable: true, name: 'label' }) label: string; - @Column({ default: false, name: 'is_remote' }) + @Column({ default: false }) isRemote: boolean; - @Column({ nullable: false, name: 'workspace_id' }) + @Column({ nullable: false }) workspaceId: string; - @CreateDateColumn({ name: 'created_at' }) + @CreateDateColumn() createdAt: Date; - @UpdateDateColumn({ name: 'updated_at' }) + @UpdateDateColumn() updatedAt: Date; } diff --git a/server/src/metadata/data-source/data-source.module.ts b/server/src/metadata/data-source/data-source.module.ts index 48cd14143bf..49f0eed71c5 100644 --- a/server/src/metadata/data-source/data-source.module.ts +++ b/server/src/metadata/data-source/data-source.module.ts @@ -1,11 +1,11 @@ import { Module } from '@nestjs/common'; - -import { DataSourceMetadataModule } from 'src/metadata/data-source-metadata/data-source-metadata.module'; +import { TypeOrmModule } from '@nestjs/typeorm'; import { DataSourceService } from './data-source.service'; +import { DataSourceEntity } from './data-source.entity'; @Module({ - imports: [DataSourceMetadataModule], + imports: [TypeOrmModule.forFeature([DataSourceEntity], 'metadata')], providers: [DataSourceService], exports: [DataSourceService], }) diff --git a/server/src/metadata/data-source/data-source.service.spec.ts b/server/src/metadata/data-source/data-source.service.spec.ts deleted file mode 100644 index a4026407fc5..00000000000 --- a/server/src/metadata/data-source/data-source.service.spec.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; - -import { EnvironmentService } from 'src/integrations/environment/environment.service'; -import { DataSourceMetadataService } from 'src/metadata/data-source-metadata/data-source-metadata.service'; - -import { DataSourceService } from './data-source.service'; - -describe('DataSourceService', () => { - let service: DataSourceService; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - DataSourceService, - { - provide: EnvironmentService, - useValue: { - getPGDatabaseUrl: () => '', - }, - }, - { - provide: DataSourceMetadataService, - useValue: {}, - }, - ], - }).compile(); - - service = module.get(DataSourceService); - }); - - it('should be defined', () => { - expect(service).toBeDefined(); - }); -}); diff --git a/server/src/metadata/data-source/data-source.service.ts b/server/src/metadata/data-source/data-source.service.ts index 60d7617662f..2d94a3458a9 100644 --- a/server/src/metadata/data-source/data-source.service.ts +++ b/server/src/metadata/data-source/data-source.service.ts @@ -1,153 +1,48 @@ -import { Injectable, OnModuleDestroy, OnModuleInit } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; -import { DataSource } from 'typeorm'; +import { Repository } from 'typeorm'; -import { EnvironmentService } from 'src/integrations/environment/environment.service'; -import { DataSourceMetadataService } from 'src/metadata/data-source-metadata/data-source-metadata.service'; -import { TenantMigration } from 'src/metadata/tenant-migration/tenant-migration.entity'; - -import { uuidToBase36 } from './data-source.util'; +import { DataSourceEntity } from './data-source.entity'; @Injectable() -export class DataSourceService implements OnModuleInit, OnModuleDestroy { - private mainDataSource: DataSource; - private dataSources: Map = new Map(); - +export class DataSourceService { constructor( - private readonly environmentService: EnvironmentService, - private readonly dataSourceMetadataService: DataSourceMetadataService, - ) { - this.mainDataSource = new DataSource({ - url: environmentService.getPGDatabaseUrl(), - type: 'postgres', - logging: false, - schema: 'public', + @InjectRepository(DataSourceEntity, 'metadata') + private readonly dataSourceMetadataRepository: Repository, + ) {} + + async createDataSourceMetadata(workspaceId: string, workspaceSchema: string) { + // TODO: Double check if this is the correct way to do this + const dataSource = await this.dataSourceMetadataRepository.findOne({ + where: { workspaceId }, + }); + + if (dataSource) { + return dataSource; + } + + return this.dataSourceMetadataRepository.save({ + workspaceId, + schema: workspaceSchema, }); } - /** - * Creates a new schema for a given workspaceId - * @param workspaceId - * @returns Promise - */ - public async createWorkspaceSchema(workspaceId: string): Promise { - const schemaName = this.getSchemaName(workspaceId); - - const queryRunner = this.mainDataSource.createQueryRunner(); - const schemaAlreadyExists = await queryRunner.hasSchema(schemaName); - - if (schemaAlreadyExists) { - throw new Error(`Schema ${schemaName} already exists`); - } - - await queryRunner.createSchema(schemaName, true); - - await queryRunner.release(); - - return schemaName; - } - - /** - * Connects to a workspace data source using the workspace metadata. Returns a cached connection if it exists. - * @param workspaceId - * @returns Promise - */ - public async connectToWorkspaceDataSource( - workspaceId: string, - ): Promise { - if (this.dataSources.has(workspaceId)) { - const cachedDataSource = this.dataSources.get(workspaceId); - return cachedDataSource; - } - - // We only want the first one for now, we will handle multiple data sources later with remote datasources. - // However, we will need to differentiate the data sources because we won't run migrations on remote data sources for example. - const dataSourceMetadata = - await this.dataSourceMetadataService.getLastDataSourceMetadataFromWorkspaceIdOrFail( - workspaceId, - ); - const schema = dataSourceMetadata.schema; - - // Probably not needed as we will ask for the schema name OR store public by default if it's remote - if (!schema && !dataSourceMetadata.isRemote) { - throw Error( - "No schema found for this non-remote data source, we don't want to fallback to public for workspace data sources.", - ); - } - - const workspaceDataSource = new DataSource({ - // TODO: We should use later dataSourceMetadata.type and use a switch case condition to create the right data source - url: dataSourceMetadata.url ?? this.environmentService.getPGDatabaseUrl(), - type: 'postgres', - logging: ['query'], - schema, - entities: { - TenantMigration, - }, + async getDataSourcesMetadataFromWorkspaceId(workspaceId: string) { + return this.dataSourceMetadataRepository.find({ + where: { workspaceId }, + order: { createdAt: 'DESC' }, }); - - await workspaceDataSource.initialize(); - - this.dataSources.set(workspaceId, workspaceDataSource); - - return workspaceDataSource; } - /** - * Disconnects from a workspace data source. - * @param workspaceId - * @returns Promise - * - */ - public async disconnectFromWorkspaceDataSource(workspaceId: string) { - if (!this.dataSources.has(workspaceId)) { - return; - } - - const dataSource = this.dataSources.get(workspaceId); - - await dataSource?.destroy(); - - this.dataSources.delete(workspaceId); + async getLastDataSourceMetadataFromWorkspaceIdOrFail(workspaceId: string) { + return this.dataSourceMetadataRepository.findOneOrFail({ + where: { workspaceId }, + order: { createdAt: 'DESC' }, + }); } - /** - * - * Returns the schema name for a given workspaceId - * @param workspaceId - * @returns string - */ - public getSchemaName(workspaceId: string): string { - return `workspace_${uuidToBase36(workspaceId)}`; - } - - public async deleteWorkspaceSchema(workspaceId: string) { - const schemaName = this.getSchemaName(workspaceId); - const queryRunner = this.mainDataSource.createQueryRunner(); - const schemaAlreadyExists = await queryRunner.hasSchema(schemaName); - - if (!schemaAlreadyExists) { - await queryRunner.release(); - return; - } - - await queryRunner.dropSchema(schemaName, true, true); - - await queryRunner.release(); - } - - async onModuleInit() { - // Init main data source "default" schema - await this.mainDataSource.initialize(); - } - - async onModuleDestroy() { - // Destroy main data source "default" schema - await this.mainDataSource.destroy(); - - // Destroy all workspace data sources - for (const [, dataSource] of this.dataSources) { - await dataSource.destroy(); - } + async delete(workspaceId: string) { + await this.dataSourceMetadataRepository.delete({ workspaceId }); } } diff --git a/server/src/metadata/data-source/data-source.util.ts b/server/src/metadata/data-source/data-source.util.ts deleted file mode 100644 index 0658a1e4684..00000000000 --- a/server/src/metadata/data-source/data-source.util.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Converts a UUID to a base 36 string. - * This is used to generate the schema name since hyphens from workspace uuid are not allowed in postgres schema names. - * - * @param uuid - * @returns - */ -export function uuidToBase36(uuid: string): string { - let devId = false; - - if (uuid.startsWith('twenty-')) { - devId = true; - // Clean dev uuids (twenty-) - uuid = uuid.replace('twenty-', ''); - } - const hexString = uuid.replace(/-/g, ''); - const base10Number = BigInt('0x' + hexString); - const base36String = base10Number.toString(36); - - return `${devId ? 'twenty_' : ''}${base36String}`; -} diff --git a/server/src/metadata/field-metadata/dtos/create-field.input.ts b/server/src/metadata/field-metadata/dtos/create-field.input.ts index 04b445aac1a..8881e6dd2bf 100644 --- a/server/src/metadata/field-metadata/dtos/create-field.input.ts +++ b/server/src/metadata/field-metadata/dtos/create-field.input.ts @@ -1,5 +1,6 @@ -import { Field, InputType } from '@nestjs/graphql'; +import { Field, HideField, InputType } from '@nestjs/graphql'; +import { BeforeCreateOne } from '@ptc-org/nestjs-query-graphql'; import { IsEnum, IsNotEmpty, @@ -8,20 +9,22 @@ import { IsUUID, } from 'class-validator'; +import { FieldMetadataTargetColumnMap } from 'src/tenant/schema-builder/interfaces/field-metadata-target-column-map.interface'; + +import { BeforeCreateOneField } from 'src/metadata/field-metadata/hooks/before-create-one-field.hook'; import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity'; @InputType() +@BeforeCreateOne(BeforeCreateOneField) export class CreateFieldInput { @IsString() @IsNotEmpty() @Field() name: string; - @IsString() @IsNotEmpty() @Field() label: string; - @IsEnum(FieldMetadataType) @IsNotEmpty() @Field(() => FieldMetadataType) @@ -29,15 +32,20 @@ export class CreateFieldInput { @IsUUID() @Field() - objectId: string; + objectMetadataId: string; @IsString() @IsOptional() @Field({ nullable: true }) description?: string; - @IsString() @IsOptional() @Field({ nullable: true }) icon?: string; + + @HideField() + targetColumnMap: FieldMetadataTargetColumnMap; + + @HideField() + workspaceId: string; } diff --git a/server/src/metadata/field-metadata/dtos/field-metadata.dto.ts b/server/src/metadata/field-metadata/dtos/field-metadata.dto.ts new file mode 100644 index 00000000000..547f96a4912 --- /dev/null +++ b/server/src/metadata/field-metadata/dtos/field-metadata.dto.ts @@ -0,0 +1,81 @@ +import { + Field, + HideField, + ID, + ObjectType, + registerEnumType, +} from '@nestjs/graphql'; + +import { + Authorize, + IDField, + QueryOptions, + Relation, +} from '@ptc-org/nestjs-query-graphql'; + +import { RelationMetadataDTO } from 'src/metadata/relation-metadata/dtos/relation-metadata.dto'; +import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity'; + +registerEnumType(FieldMetadataType, { + name: 'FieldMetadataType', + description: 'Type of the field', +}); + +@ObjectType('field') +@Authorize({ + authorize: (context: any) => ({ + workspaceId: { eq: context?.req?.user?.workspace?.id }, + }), +}) +@QueryOptions({ + defaultResultSize: 10, + disableFilter: true, + disableSort: true, + maxResultsSize: 1000, +}) +@Relation('toRelationMetadata', () => RelationMetadataDTO, { + nullable: true, +}) +@Relation('fromRelationMetadata', () => RelationMetadataDTO, { + nullable: true, +}) +export class FieldMetadataDTO { + @IDField(() => ID) + id: string; + + @Field(() => FieldMetadataType) + type: FieldMetadataType; + + @Field() + name: string; + + @Field() + label: string; + + @Field({ nullable: true }) + description: string; + + @Field({ nullable: true }) + icon: string; + + @Field({ nullable: true, deprecationReason: 'Use label name instead' }) + placeholder?: string; + + @Field() + isCustom: boolean; + + @Field() + isActive: boolean; + + @Field() + isNullable: boolean; + + @HideField() + workspaceId: string; + + @Field() + createdAt: Date; + + @Field() + updatedAt: Date; +} diff --git a/server/src/metadata/field-metadata/field-metadata.auto-resolver-opts.ts b/server/src/metadata/field-metadata/field-metadata.auto-resolver-opts.ts deleted file mode 100644 index 66f3c53dac8..00000000000 --- a/server/src/metadata/field-metadata/field-metadata.auto-resolver-opts.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { SortDirection } from '@ptc-org/nestjs-query-core'; -import { - AutoResolverOpts, - PagingStrategies, - ReadResolverOpts, -} from '@ptc-org/nestjs-query-graphql'; - -import { JwtAuthGuard } from 'src/guards/jwt.auth.guard'; - -import { FieldMetadata } from './field-metadata.entity'; - -import { FieldMetadataService } from './services/field-metadata.service'; -import { CreateFieldInput } from './dtos/create-field.input'; -import { UpdateFieldInput } from './dtos/update-field.input'; - -export const fieldMetadataAutoResolverOpts: AutoResolverOpts< - any, - any, - unknown, - unknown, - ReadResolverOpts, - PagingStrategies ->[] = [ - { - EntityClass: FieldMetadata, - DTOClass: FieldMetadata, - CreateDTOClass: CreateFieldInput, - UpdateDTOClass: UpdateFieldInput, - ServiceClass: FieldMetadataService, - enableTotalCount: true, - pagingStrategy: PagingStrategies.CURSOR, - read: { - defaultSort: [{ field: 'id', direction: SortDirection.DESC }], - }, - create: { - many: { disabled: true }, - }, - update: { - many: { disabled: true }, - }, - delete: { many: { disabled: true } }, - guards: [JwtAuthGuard], - }, -]; diff --git a/server/src/metadata/field-metadata/field-metadata.entity.ts b/server/src/metadata/field-metadata/field-metadata.entity.ts index 0d78f56e803..5db8e0d4d4b 100644 --- a/server/src/metadata/field-metadata/field-metadata.entity.ts +++ b/server/src/metadata/field-metadata/field-metadata.entity.ts @@ -1,31 +1,20 @@ -import { Field, ID, ObjectType, registerEnumType } from '@nestjs/graphql'; - import { - Column, - CreateDateColumn, Entity, - JoinColumn, - ManyToOne, - OneToOne, - PrimaryGeneratedColumn, Unique, + PrimaryGeneratedColumn, + Column, + ManyToOne, + JoinColumn, + OneToOne, + CreateDateColumn, UpdateDateColumn, } from 'typeorm'; -import { - Authorize, - BeforeCreateOne, - IDField, - QueryOptions, - Relation, -} from '@ptc-org/nestjs-query-graphql'; import { FieldMetadataInterface } from 'src/tenant/schema-builder/interfaces/field-metadata.interface'; +import { FieldMetadataTargetColumnMap } from 'src/tenant/schema-builder/interfaces/field-metadata-target-column-map.interface'; -import { ObjectMetadata } from 'src/metadata/object-metadata/object-metadata.entity'; -import { RelationMetadata } from 'src/metadata/relation-metadata/relation-metadata.entity'; - -import { BeforeCreateOneField } from './hooks/before-create-one-field.hook'; -import { FieldMetadataTargetColumnMap } from './interfaces/field-metadata-target-column-map.interface'; +import { ObjectMetadataEntity } from 'src/metadata/object-metadata/object-metadata.entity'; +import { RelationMetadataEntity } from 'src/metadata/relation-metadata/relation-metadata.entity'; export enum FieldMetadataType { UUID = 'uuid', @@ -41,101 +30,73 @@ export enum FieldMetadataType { RELATION = 'RELATION', } -registerEnumType(FieldMetadataType, { - name: 'FieldMetadataType', - description: 'Type of the field', -}); - -@Entity('field_metadata') -@ObjectType('field') -@BeforeCreateOne(BeforeCreateOneField) -@Authorize({ - authorize: (context: any) => ({ - workspaceId: { eq: context?.req?.user?.workspace?.id }, - }), -}) -@QueryOptions({ - defaultResultSize: 10, - disableFilter: true, - disableSort: true, - maxResultsSize: 1000, -}) -@Unique('IndexOnNameObjectIdAndWorkspaceIdUnique', [ +@Entity('fieldMetadata') +@Unique('IndexOnNameObjectMetadataIdAndWorkspaceIdUnique', [ 'name', - 'objectId', + 'objectMetadataId', 'workspaceId', ]) -@Relation('toRelationMetadata', () => RelationMetadata, { nullable: true }) -@Relation('fromRelationMetadata', () => RelationMetadata, { nullable: true }) -export class FieldMetadata implements FieldMetadataInterface { - @IDField(() => ID) +export class FieldMetadataEntity implements FieldMetadataInterface { @PrimaryGeneratedColumn('uuid') id: string; - @Column({ nullable: false, name: 'object_id' }) - objectId: string; + @Column({ nullable: false, type: 'uuid' }) + objectMetadataId: string; + + @ManyToOne(() => ObjectMetadataEntity, (object) => object.fields, { + onDelete: 'CASCADE', + }) + @JoinColumn({ name: 'objectMetadataId' }) + object: ObjectMetadataEntity; - @Field(() => FieldMetadataType) @Column({ nullable: false }) type: FieldMetadataType; - @Field() @Column({ nullable: false }) name: string; - @Field() @Column({ nullable: false }) label: string; - @Column({ nullable: false, name: 'target_column_map', type: 'jsonb' }) + @Column({ nullable: false, type: 'jsonb' }) targetColumnMap: FieldMetadataTargetColumnMap; - @Field({ nullable: true }) - @Column({ nullable: true, name: 'description', type: 'text' }) + @Column({ nullable: true, type: 'text' }) description: string; - @Field({ nullable: true }) - @Column({ nullable: true, name: 'icon' }) + @Column({ nullable: true }) icon: string; - @Field({ nullable: true, deprecationReason: 'Use label name instead' }) - placeholder: string; - @Column('text', { nullable: true, array: true }) enums: string[]; - @Field() - @Column({ default: false, name: 'is_custom' }) + @Column({ default: false }) isCustom: boolean; - @Field() - @Column({ default: false, name: 'is_active' }) + @Column({ default: false }) isActive: boolean; - @Field() - @Column({ nullable: true, default: true, name: 'is_nullable' }) + @Column({ nullable: true, default: true }) isNullable: boolean; - @Column({ nullable: false, name: 'workspace_id' }) + @Column({ nullable: false }) workspaceId: string; - @ManyToOne(() => ObjectMetadata, (object) => object.fields, { - onDelete: 'CASCADE', - }) - @JoinColumn({ name: 'object_id' }) - object: ObjectMetadata; + @OneToOne( + () => RelationMetadataEntity, + (relation: RelationMetadataEntity) => relation.fromFieldMetadata, + ) + fromRelationMetadata: RelationMetadataEntity; - @OneToOne(() => RelationMetadata, (relation) => relation.fromFieldMetadata) - fromRelationMetadata: RelationMetadata; + @OneToOne( + () => RelationMetadataEntity, + (relation: RelationMetadataEntity) => relation.toFieldMetadata, + ) + toRelationMetadata: RelationMetadataEntity; - @OneToOne(() => RelationMetadata, (relation) => relation.toFieldMetadata) - toRelationMetadata: RelationMetadata; - - @Field() - @CreateDateColumn({ name: 'created_at' }) + @CreateDateColumn() createdAt: Date; - @Field() - @UpdateDateColumn({ name: 'updated_at' }) + @UpdateDateColumn() updatedAt: Date; } diff --git a/server/src/metadata/field-metadata/field-metadata.module.ts b/server/src/metadata/field-metadata/field-metadata.module.ts index 45b1ae6260e..0ca77546b6f 100644 --- a/server/src/metadata/field-metadata/field-metadata.module.ts +++ b/server/src/metadata/field-metadata/field-metadata.module.ts @@ -1,28 +1,56 @@ import { Module } from '@nestjs/common'; -import { NestjsQueryGraphQLModule } from '@ptc-org/nestjs-query-graphql'; +import { + NestjsQueryGraphQLModule, + PagingStrategies, +} from '@ptc-org/nestjs-query-graphql'; import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm'; +import { SortDirection } from '@ptc-org/nestjs-query-core'; -import { MigrationRunnerModule } from 'src/metadata/migration-runner/migration-runner.module'; +import { TenantMigrationRunnerModule } from 'src/tenant-migration-runner/tenant-migration-runner.module'; import { TenantMigrationModule } from 'src/metadata/tenant-migration/tenant-migration.module'; import { ObjectMetadataModule } from 'src/metadata/object-metadata/object-metadata.module'; +import { JwtAuthGuard } from 'src/guards/jwt.auth.guard'; -import { FieldMetadata } from './field-metadata.entity'; -import { fieldMetadataAutoResolverOpts } from './field-metadata.auto-resolver-opts'; +import { FieldMetadataService } from './field-metadata.service'; +import { FieldMetadataEntity } from './field-metadata.entity'; -import { FieldMetadataService } from './services/field-metadata.service'; +import { CreateFieldInput } from './dtos/create-field.input'; +import { FieldMetadataDTO } from './dtos/field-metadata.dto'; +import { UpdateFieldInput } from './dtos/update-field.input'; @Module({ imports: [ NestjsQueryGraphQLModule.forFeature({ imports: [ - NestjsQueryTypeOrmModule.forFeature([FieldMetadata], 'metadata'), + NestjsQueryTypeOrmModule.forFeature([FieldMetadataEntity], 'metadata'), TenantMigrationModule, - MigrationRunnerModule, + TenantMigrationRunnerModule, ObjectMetadataModule, ], services: [FieldMetadataService], - resolvers: fieldMetadataAutoResolverOpts, + resolvers: [ + { + EntityClass: FieldMetadataEntity, + DTOClass: FieldMetadataDTO, + CreateDTOClass: CreateFieldInput, + UpdateDTOClass: UpdateFieldInput, + ServiceClass: FieldMetadataService, + enableTotalCount: true, + pagingStrategy: PagingStrategies.CURSOR, + read: { + defaultSort: [{ field: 'id', direction: SortDirection.DESC }], + }, + create: { + many: { disabled: true }, + }, + update: { + many: { disabled: true }, + }, + delete: { many: { disabled: true } }, + guards: [JwtAuthGuard], + }, + ], }), ], providers: [FieldMetadataService], diff --git a/server/src/metadata/field-metadata/services/field-metadata.service.ts b/server/src/metadata/field-metadata/field-metadata.service.ts similarity index 75% rename from server/src/metadata/field-metadata/services/field-metadata.service.ts rename to server/src/metadata/field-metadata/field-metadata.service.ts index 36a41854152..59bc2a04e25 100644 --- a/server/src/metadata/field-metadata/services/field-metadata.service.ts +++ b/server/src/metadata/field-metadata/field-metadata.service.ts @@ -10,37 +10,39 @@ import { Repository } from 'typeorm'; import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm'; import { DeleteOneOptions } from '@ptc-org/nestjs-query-core'; -import { FieldMetadata } from 'src/metadata/field-metadata/field-metadata.entity'; import { convertFieldMetadataToColumnActions, generateTargetColumnMap, } from 'src/metadata/field-metadata/utils/field-metadata.util'; -import { MigrationRunnerService } from 'src/metadata/migration-runner/migration-runner.service'; +import { TenantMigrationRunnerService } from 'src/tenant-migration-runner/tenant-migration-runner.service'; import { TenantMigrationService } from 'src/metadata/tenant-migration/tenant-migration.service'; -import { ObjectMetadataService } from 'src/metadata/object-metadata/services/object-metadata.service'; +import { ObjectMetadataService } from 'src/metadata/object-metadata/object-metadata.service'; +import { FieldMetadataDTO } from 'src/metadata/field-metadata/dtos/field-metadata.dto'; +import { CreateFieldInput } from 'src/metadata/field-metadata/dtos/create-field.input'; import { TenantMigrationTableAction } from 'src/metadata/tenant-migration/tenant-migration.entity'; +import { FieldMetadataEntity } from './field-metadata.entity'; + @Injectable() -export class FieldMetadataService extends TypeOrmQueryService { +export class FieldMetadataService extends TypeOrmQueryService { constructor( - @InjectRepository(FieldMetadata, 'metadata') - private readonly fieldMetadataRepository: Repository, + @InjectRepository(FieldMetadataEntity, 'metadata') + private readonly fieldMetadataRepository: Repository, private readonly objectMetadataService: ObjectMetadataService, private readonly tenantMigrationService: TenantMigrationService, - private readonly migrationRunnerService: MigrationRunnerService, + private readonly migrationRunnerService: TenantMigrationRunnerService, ) { super(fieldMetadataRepository); } override async deleteOne( id: string, - opts?: DeleteOneOptions | undefined, - ): Promise { + opts?: DeleteOneOptions | undefined, + ): Promise { const fieldMetadata = await this.fieldMetadataRepository.findOne({ where: { id }, }); - if (!fieldMetadata) { throw new NotFoundException('Field does not exist'); } @@ -58,10 +60,12 @@ export class FieldMetadataService extends TypeOrmQueryService { return super.deleteOne(id, opts); } - override async createOne(record: FieldMetadata): Promise { + override async createOne( + record: CreateFieldInput, + ): Promise { const objectMetadata = await this.objectMetadataService.findOneWithinWorkspace( - record.objectId, + record.objectMetadataId, record.workspaceId, ); @@ -72,7 +76,7 @@ export class FieldMetadataService extends TypeOrmQueryService { const fieldAlreadyExists = await this.fieldMetadataRepository.findOne({ where: { name: record.name, - objectId: record.objectId, + objectMetadataId: record.objectMetadataId, workspaceId: record.workspaceId, }, }); @@ -83,11 +87,9 @@ export class FieldMetadataService extends TypeOrmQueryService { const createdFieldMetadata = await super.createOne({ ...record, - targetColumnMap: generateTargetColumnMap( - record.type, - record.isCustom, - record.name, - ), + targetColumnMap: generateTargetColumnMap(record.type, true, record.name), + isActive: true, + isCustom: true, }); await this.tenantMigrationService.createCustomMigration( diff --git a/server/src/metadata/field-metadata/hooks/before-create-one-field.hook.ts b/server/src/metadata/field-metadata/hooks/before-create-one-field.hook.ts index 1d44025785c..9eb09c592ac 100644 --- a/server/src/metadata/field-metadata/hooks/before-create-one-field.hook.ts +++ b/server/src/metadata/field-metadata/hooks/before-create-one-field.hook.ts @@ -5,10 +5,10 @@ import { CreateOneInputType, } from '@ptc-org/nestjs-query-graphql'; -import { FieldMetadata } from 'src/metadata/field-metadata/field-metadata.entity'; +import { CreateFieldInput } from 'src/metadata/field-metadata/dtos/create-field.input'; @Injectable() -export class BeforeCreateOneField +export class BeforeCreateOneField implements BeforeCreateOneHook { async run( @@ -16,14 +16,11 @@ export class BeforeCreateOneField context: any, ): Promise> { const workspaceId = context?.req?.user?.workspace?.id; - if (!workspaceId) { throw new UnauthorizedException(); } instance.input.workspaceId = workspaceId; - instance.input.isActive = true; - instance.input.isCustom = true; return instance; } } diff --git a/server/src/metadata/field-metadata/services/field-metadata.service.spec.ts b/server/src/metadata/field-metadata/services/field-metadata.service.spec.ts deleted file mode 100644 index 87b62cad30b..00000000000 --- a/server/src/metadata/field-metadata/services/field-metadata.service.spec.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { getRepositoryToken } from '@nestjs/typeorm'; - -import { FieldMetadata } from 'src/metadata/field-metadata/field-metadata.entity'; -import { MigrationRunnerService } from 'src/metadata/migration-runner/migration-runner.service'; -import { TenantMigrationService } from 'src/metadata/tenant-migration/tenant-migration.service'; -import { ObjectMetadataService } from 'src/metadata/object-metadata/services/object-metadata.service'; - -import { FieldMetadataService } from './field-metadata.service'; - -describe('FieldMetadataService', () => { - let service: FieldMetadataService; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - FieldMetadataService, - { - provide: getRepositoryToken(FieldMetadata, 'metadata'), - useValue: {}, - }, - { - provide: ObjectMetadataService, - useValue: {}, - }, - { - provide: TenantMigrationService, - useValue: {}, - }, - { - provide: MigrationRunnerService, - useValue: {}, - }, - ], - }).compile(); - - service = module.get(FieldMetadataService); - }); - - it('should be defined', () => { - expect(service).toBeDefined(); - }); -}); diff --git a/server/src/metadata/field-metadata/utils/field-metadata.util.ts b/server/src/metadata/field-metadata/utils/field-metadata.util.ts index 1e55fbf5c28..b0819d00cf9 100644 --- a/server/src/metadata/field-metadata/utils/field-metadata.util.ts +++ b/server/src/metadata/field-metadata/utils/field-metadata.util.ts @@ -1,7 +1,7 @@ -import { FieldMetadataTargetColumnMap } from 'src/metadata/field-metadata/interfaces/field-metadata-target-column-map.interface'; +import { FieldMetadataTargetColumnMap } from 'src/tenant/schema-builder/interfaces/field-metadata-target-column-map.interface'; import { - FieldMetadata, + FieldMetadataEntity, FieldMetadataType, } from 'src/metadata/field-metadata/field-metadata.entity'; import { @@ -49,7 +49,7 @@ export function generateTargetColumnMap( } export function convertFieldMetadataToColumnActions( - fieldMetadata: FieldMetadata, + fieldMetadata: FieldMetadataEntity, ): TenantMigrationColumnAction[] { switch (fieldMetadata.type) { case FieldMetadataType.TEXT: diff --git a/server/src/metadata/metadata.module.ts b/server/src/metadata/metadata.module.ts index d7461d6b785..a462f88c521 100644 --- a/server/src/metadata/metadata.module.ts +++ b/server/src/metadata/metadata.module.ts @@ -1,32 +1,18 @@ import { Module } from '@nestjs/common'; -import { TypeOrmModule, TypeOrmModuleOptions } from '@nestjs/typeorm'; import { GraphQLModule } from '@nestjs/graphql'; import { YogaDriverConfig, YogaDriver } from '@graphql-yoga/nestjs'; import GraphQLJSON from 'graphql-type-json'; -import { MigrationRunnerModule } from 'src/metadata/migration-runner/migration-runner.module'; +import { TenantMigrationRunnerModule } from 'src/tenant-migration-runner/tenant-migration-runner.module'; import { TenantMigrationModule } from 'src/metadata/tenant-migration/tenant-migration.module'; -import { typeORMMetadataModuleOptions } from './metadata.datasource'; - import { DataSourceModule } from './data-source/data-source.module'; -import { DataSourceMetadataModule } from './data-source-metadata/data-source-metadata.module'; import { FieldMetadataModule } from './field-metadata/field-metadata.module'; import { ObjectMetadataModule } from './object-metadata/object-metadata.module'; import { RelationMetadataModule } from './relation-metadata/relation-metadata.module'; - -const typeORMFactory = async (): Promise => ({ - ...typeORMMetadataModuleOptions, - name: 'metadata', -}); - @Module({ imports: [ - TypeOrmModule.forRootAsync({ - useFactory: typeORMFactory, - name: 'metadata', - }), GraphQLModule.forRoot({ context: ({ req }) => ({ req }), driver: YogaDriver, @@ -37,10 +23,9 @@ const typeORMFactory = async (): Promise => ({ path: '/metadata', }), DataSourceModule, - DataSourceMetadataModule, FieldMetadataModule, ObjectMetadataModule, - MigrationRunnerModule, + TenantMigrationRunnerModule, TenantMigrationModule, RelationMetadataModule, ], diff --git a/server/src/metadata/migration-runner/migration-runner.module.ts b/server/src/metadata/migration-runner/migration-runner.module.ts deleted file mode 100644 index c042a7671d3..00000000000 --- a/server/src/metadata/migration-runner/migration-runner.module.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Module } from '@nestjs/common'; - -import { DataSourceModule } from 'src/metadata/data-source/data-source.module'; -import { TenantMigrationModule } from 'src/metadata/tenant-migration/tenant-migration.module'; - -import { MigrationRunnerService } from './migration-runner.service'; - -@Module({ - imports: [DataSourceModule, TenantMigrationModule], - exports: [MigrationRunnerService], - providers: [MigrationRunnerService], -}) -export class MigrationRunnerModule {} diff --git a/server/src/metadata/migration-runner/migration-runner.service.spec.ts b/server/src/metadata/migration-runner/migration-runner.service.spec.ts deleted file mode 100644 index e97caafe2c4..00000000000 --- a/server/src/metadata/migration-runner/migration-runner.service.spec.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; - -import { DataSourceService } from 'src/metadata/data-source/data-source.service'; -import { TenantMigrationService } from 'src/metadata/tenant-migration/tenant-migration.service'; - -import { MigrationRunnerService } from './migration-runner.service'; - -describe('MigrationRunnerService', () => { - let service: MigrationRunnerService; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - MigrationRunnerService, - { - provide: DataSourceService, - useValue: {}, - }, - { - provide: TenantMigrationService, - useValue: {}, - }, - ], - }).compile(); - - service = module.get(MigrationRunnerService); - }); - - it('should be defined', () => { - expect(service).toBeDefined(); - }); -}); diff --git a/server/src/metadata/migrations/1695214465080-InitMetadataTables.ts b/server/src/metadata/migrations/1695214465080-InitMetadataTables.ts deleted file mode 100644 index 13647df58db..00000000000 --- a/server/src/metadata/migrations/1695214465080-InitMetadataTables.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class InitMetadataTables1695214465080 implements MigrationInterface { - name = 'InitMetadataTables1695214465080'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE TYPE "metadata"."data_source_metadata_type_enum" AS ENUM ('postgres', 'mysql');`, - ); - await queryRunner.query( - `CREATE TABLE "metadata"."data_source_metadata" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "url" character varying, "schema" character varying, "type" "metadata"."data_source_metadata_type_enum" NOT NULL DEFAULT 'postgres', "display_name" character varying, "is_remote" boolean NOT NULL DEFAULT false, "workspace_id" character varying NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT now(), "updated_at" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "PK_923752b7e62a300a4969bd0e038" PRIMARY KEY ("id"))`, - ); - await queryRunner.query( - `CREATE TABLE "metadata"."field_metadata" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "object_id" uuid NOT NULL, "type" character varying NOT NULL, "display_name" character varying NOT NULL, "target_column_name" character varying NOT NULL, "is_custom" boolean NOT NULL DEFAULT false, "workspace_id" character varying NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT now(), "updated_at" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "PK_c75db587904cad6af109b5c65f1" PRIMARY KEY ("id"))`, - ); - await queryRunner.query( - `CREATE TABLE "metadata"."object_metadata" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "data_source_id" character varying NOT NULL, "display_name" character varying NOT NULL, "target_table_name" character varying NOT NULL, "is_custom" boolean NOT NULL DEFAULT false, "workspace_id" character varying NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT now(), "updated_at" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "PK_c8c5f885767b356949c18c201c1" PRIMARY KEY ("id"))`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" ADD CONSTRAINT "FK_38179b299795e48887fc99f937a" FOREIGN KEY ("object_id") REFERENCES "metadata"."object_metadata"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" DROP CONSTRAINT "FK_38179b299795e48887fc99f937a"`, - ); - await queryRunner.query(`DROP TABLE "metadata"."object_metadata"`); - await queryRunner.query(`DROP TABLE "metadata"."field_metadata"`); - await queryRunner.query(`DROP TABLE "metadata"."data_source_metadata"`); - } -} diff --git a/server/src/metadata/migrations/1695717691800-alter-field-metadata-table.ts b/server/src/metadata/migrations/1695717691800-alter-field-metadata-table.ts deleted file mode 100644 index 4483db8dfe0..00000000000 --- a/server/src/metadata/migrations/1695717691800-alter-field-metadata-table.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AlterFieldMetadataTable1695717691800 - implements MigrationInterface -{ - name = 'AlterFieldMetadataTable1695717691800'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" ADD "enums" text array`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" ADD "is_nullable" boolean DEFAULT true`, - ); - await queryRunner.query( - `ALTER TYPE "metadata"."data_source_metadata_type_enum" RENAME TO "data_source_metadata_type_enum_old"`, - ); - await queryRunner.query( - `CREATE TYPE "metadata"."data_source_metadata_type_enum" AS ENUM('postgres')`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."data_source_metadata" ALTER COLUMN "type" DROP DEFAULT`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."data_source_metadata" ALTER COLUMN "type" TYPE "metadata"."data_source_metadata_type_enum" USING "type"::"text"::"metadata"."data_source_metadata_type_enum"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."data_source_metadata" ALTER COLUMN "type" SET DEFAULT 'postgres'`, - ); - await queryRunner.query( - `DROP TYPE "metadata"."data_source_metadata_type_enum_old"`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE TYPE "metadata"."data_source_metadata_type_enum_old" AS ENUM('postgres', 'mysql')`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."data_source_metadata" ALTER COLUMN "type" DROP DEFAULT`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."data_source_metadata" ALTER COLUMN "type" TYPE "metadata"."data_source_metadata_type_enum_old" USING "type"::"text"::"metadata"."data_source_metadata_type_enum_old"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."data_source_metadata" ALTER COLUMN "type" SET DEFAULT 'postgres'`, - ); - await queryRunner.query( - `DROP TYPE "metadata"."data_source_metadata_type_enum"`, - ); - await queryRunner.query( - `ALTER TYPE "metadata"."data_source_metadata_type_enum_old" RENAME TO "data_source_metadata_type_enum"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" DROP COLUMN "is_nullable"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" DROP COLUMN "enums"`, - ); - } -} diff --git a/server/src/metadata/migrations/1696409050890-add-target-column-map.ts b/server/src/metadata/migrations/1696409050890-add-target-column-map.ts deleted file mode 100644 index 23da21eb017..00000000000 --- a/server/src/metadata/migrations/1696409050890-add-target-column-map.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddTargetColumnMap1696409050890 implements MigrationInterface { - name = 'AddTargetColumnMap1696409050890'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" ADD "description" text`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" ADD "icon" character varying`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" ADD "placeholder" character varying`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" ADD "target_column_map" jsonb`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" ADD "is_active" boolean NOT NULL DEFAULT false`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."object_metadata" ADD "display_name_singular" character varying`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."object_metadata" ADD "display_name_plural" character varying`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."object_metadata" ADD "description" text`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."object_metadata" ADD "icon" character varying`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."object_metadata" ADD "is_active" boolean NOT NULL DEFAULT false`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "metadata"."object_metadata" DROP COLUMN "is_active"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."object_metadata" DROP COLUMN "icon"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."object_metadata" DROP COLUMN "description"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."object_metadata" DROP COLUMN "display_name_plural"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."object_metadata" DROP COLUMN "display_name_singular"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" DROP COLUMN "is_active"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" DROP COLUMN "target_column_map"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" DROP COLUMN "placeholder"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" DROP COLUMN "icon"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" DROP COLUMN "description"`, - ); - } -} diff --git a/server/src/metadata/migrations/1697126636202-MetadataNameLabelRefactoring.ts b/server/src/metadata/migrations/1697126636202-MetadataNameLabelRefactoring.ts deleted file mode 100644 index 1f0bc5094cd..00000000000 --- a/server/src/metadata/migrations/1697126636202-MetadataNameLabelRefactoring.ts +++ /dev/null @@ -1,149 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class MetadataNameLabelRefactoring1697126636202 - implements MigrationInterface -{ - name = 'MetadataNameLabelRefactoring1697126636202'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "metadata"."data_source_metadata" RENAME COLUMN "display_name" TO "label"`, - ); - await queryRunner.query( - `CREATE TABLE "metadata"."tenant_migrations" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "migrations" jsonb, "applied_at" TIMESTAMP, "created_at" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "PK_cb644cbc7f5092850f25eecb465" PRIMARY KEY ("id"))`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."object_metadata" DROP COLUMN "display_name"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."object_metadata" DROP COLUMN "display_name_singular"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."object_metadata" DROP COLUMN "display_name_plural"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" DROP COLUMN "display_name"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" DROP COLUMN "target_column_name"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."object_metadata" ADD "name_singular" character varying NOT NULL`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."object_metadata" ADD CONSTRAINT "UQ_8b063d2a685474dbae56cd685d2" UNIQUE ("name_singular")`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."object_metadata" ADD "name_plural" character varying NOT NULL`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."object_metadata" ADD CONSTRAINT "UQ_a2387e1b21120110b7e3db83da1" UNIQUE ("name_plural")`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."object_metadata" ADD "label_singular" character varying NOT NULL`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."object_metadata" ADD "label_plural" character varying NOT NULL`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" ADD "name_singular" character varying NOT NULL`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" ADD "name_plural" character varying`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" ADD "label_singular" character varying NOT NULL`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" ADD "label_plural" character varying`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."data_source_metadata" ALTER COLUMN "id" SET DEFAULT uuid_generate_v4()`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" DROP CONSTRAINT "FK_38179b299795e48887fc99f937a"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."object_metadata" ALTER COLUMN "id" SET DEFAULT uuid_generate_v4()`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" ALTER COLUMN "id" SET DEFAULT uuid_generate_v4()`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" ALTER COLUMN "target_column_map" SET NOT NULL`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" ADD CONSTRAINT "FK_38179b299795e48887fc99f937a" FOREIGN KEY ("object_id") REFERENCES "metadata"."object_metadata"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" DROP CONSTRAINT "FK_38179b299795e48887fc99f937a"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" ALTER COLUMN "target_column_map" DROP NOT NULL`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" ALTER COLUMN "id" DROP DEFAULT`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."object_metadata" ALTER COLUMN "id" DROP DEFAULT`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" ADD CONSTRAINT "FK_38179b299795e48887fc99f937a" FOREIGN KEY ("object_id") REFERENCES "metadata"."object_metadata"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."data_source_metadata" ALTER COLUMN "id" DROP DEFAULT`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" DROP COLUMN "label_plural"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" DROP COLUMN "label_singular"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" DROP COLUMN "name_plural"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" DROP COLUMN "name_singular"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."object_metadata" DROP COLUMN "label_plural"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."object_metadata" DROP COLUMN "label_singular"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."object_metadata" DROP CONSTRAINT "UQ_a2387e1b21120110b7e3db83da1"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."object_metadata" DROP COLUMN "name_plural"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."object_metadata" DROP CONSTRAINT "UQ_8b063d2a685474dbae56cd685d2"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."object_metadata" DROP COLUMN "name_singular"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" ADD "target_column_name" character varying NOT NULL`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" ADD "display_name" character varying NOT NULL`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."object_metadata" ADD "display_name_plural" character varying`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."object_metadata" ADD "display_name_singular" character varying`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."object_metadata" ADD "display_name" character varying NOT NULL`, - ); - await queryRunner.query(`DROP TABLE "metadata"."tenant_migrations"`); - await queryRunner.query( - `ALTER TABLE "metadata"."data_source_metadata" RENAME COLUMN "label" TO "display_name"`, - ); - } -} diff --git a/server/src/metadata/migrations/1697471445015-removeFieldMetadataPlaceholder.ts b/server/src/metadata/migrations/1697471445015-removeFieldMetadataPlaceholder.ts deleted file mode 100644 index b1235e2e56c..00000000000 --- a/server/src/metadata/migrations/1697471445015-removeFieldMetadataPlaceholder.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveFieldMetadataPlaceholder1697471445015 - implements MigrationInterface -{ - name = 'RemoveFieldMetadataPlaceholder1697471445015'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" DROP COLUMN "placeholder"`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" ADD "placeholder" character varying`, - ); - } -} diff --git a/server/src/metadata/migrations/1697474804403-addSoftDelete.ts b/server/src/metadata/migrations/1697474804403-addSoftDelete.ts deleted file mode 100644 index 675525c67f8..00000000000 --- a/server/src/metadata/migrations/1697474804403-addSoftDelete.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddSoftDelete1697474804403 implements MigrationInterface { - name = 'AddSoftDelete1697474804403'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "metadata"."object_metadata" ADD "deleted_at" TIMESTAMP`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" ADD "deleted_at" TIMESTAMP`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" DROP COLUMN "deleted_at"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."object_metadata" DROP COLUMN "deleted_at"`, - ); - } -} diff --git a/server/src/metadata/migrations/1697534910933-removeSingularPluralFromFieldLabelAndName.ts b/server/src/metadata/migrations/1697534910933-removeSingularPluralFromFieldLabelAndName.ts deleted file mode 100644 index e8a3515135f..00000000000 --- a/server/src/metadata/migrations/1697534910933-removeSingularPluralFromFieldLabelAndName.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveSingularPluralFromFieldLabelAndName1697534910933 - implements MigrationInterface -{ - name = 'RemoveSingularPluralFromFieldLabelAndName1697534910933'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" DROP COLUMN "name_singular"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" DROP COLUMN "name_plural"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" DROP COLUMN "label_singular"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" DROP COLUMN "label_plural"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" ADD "name" character varying NOT NULL`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" ADD "label" character varying NOT NULL`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" DROP COLUMN "label"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" DROP COLUMN "name"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" ADD "label_plural" character varying`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" ADD "label_singular" character varying NOT NULL`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" ADD "name_plural" character varying`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" ADD "name_singular" character varying NOT NULL`, - ); - } -} diff --git a/server/src/metadata/migrations/1697622715467-addNameAndIsCustomToTenantMigration.ts b/server/src/metadata/migrations/1697622715467-addNameAndIsCustomToTenantMigration.ts deleted file mode 100644 index 7e828d9f2a7..00000000000 --- a/server/src/metadata/migrations/1697622715467-addNameAndIsCustomToTenantMigration.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddNameAndIsCustomToTenantMigration1697622715467 - implements MigrationInterface -{ - name = 'AddNameAndIsCustomToTenantMigration1697622715467'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "metadata"."tenant_migrations" DROP COLUMN "applied_at"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."tenant_migrations" DROP COLUMN "created_at"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."tenant_migrations" ADD "name" character varying`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."tenant_migrations" ADD "isCustom" boolean NOT NULL DEFAULT false`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."tenant_migrations" ADD "appliedAt" TIMESTAMP`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."tenant_migrations" ADD "workspaceId" character varying NOT NULL`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."tenant_migrations" ADD "createdAt" TIMESTAMP NOT NULL DEFAULT now()`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "metadata"."tenant_migrations" DROP COLUMN "createdAt"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."tenant_migrations" DROP COLUMN "workspaceId"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."tenant_migrations" DROP COLUMN "appliedAt"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."tenant_migrations" DROP COLUMN "isCustom"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."tenant_migrations" DROP COLUMN "name"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."tenant_migrations" ADD "created_at" TIMESTAMP NOT NULL DEFAULT now()`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."tenant_migrations" ADD "applied_at" TIMESTAMP`, - ); - } -} diff --git a/server/src/metadata/migrations/1697630766924-addUniqueConstraintsOnFieldObjectMetadata.ts b/server/src/metadata/migrations/1697630766924-addUniqueConstraintsOnFieldObjectMetadata.ts deleted file mode 100644 index cf1b5803723..00000000000 --- a/server/src/metadata/migrations/1697630766924-addUniqueConstraintsOnFieldObjectMetadata.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddUniqueConstraintsOnFieldObjectMetadata1697630766924 - implements MigrationInterface -{ - name = 'AddUniqueConstraintsOnFieldObjectMetadata1697630766924'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "metadata"."object_metadata" DROP CONSTRAINT "UQ_8b063d2a685474dbae56cd685d2"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."object_metadata" DROP CONSTRAINT "UQ_a2387e1b21120110b7e3db83da1"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" ADD CONSTRAINT "IndexOnNameObjectIdAndWorkspaceIdUnique" UNIQUE ("name", "object_id", "workspace_id")`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."object_metadata" ADD CONSTRAINT "IndexOnNamePluralAndWorkspaceIdUnique" UNIQUE ("name_plural", "workspace_id")`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."object_metadata" ADD CONSTRAINT "IndexOnNameSingularAndWorkspaceIdUnique" UNIQUE ("name_singular", "workspace_id")`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "metadata"."object_metadata" DROP CONSTRAINT "IndexOnNameSingularAndWorkspaceIdUnique"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."object_metadata" DROP CONSTRAINT "IndexOnNamePluralAndWorkspaceIdUnique"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" DROP CONSTRAINT "IndexOnNameAndWorkspaceIdUnique"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."object_metadata" ADD CONSTRAINT "UQ_a2387e1b21120110b7e3db83da1" UNIQUE ("name_plural")`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."object_metadata" ADD CONSTRAINT "UQ_8b063d2a685474dbae56cd685d2" UNIQUE ("name_singular")`, - ); - } -} diff --git a/server/src/metadata/migrations/1698328717102-removeMetadataSoftDelete.ts b/server/src/metadata/migrations/1698328717102-removeMetadataSoftDelete.ts deleted file mode 100644 index 1f960e71162..00000000000 --- a/server/src/metadata/migrations/1698328717102-removeMetadataSoftDelete.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class RemoveMetadataSoftDelete1698328717102 - implements MigrationInterface -{ - name = 'RemoveMetadataSoftDelete1698328717102'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" DROP CONSTRAINT "FK_38179b299795e48887fc99f937a"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."object_metadata" DROP COLUMN "deleted_at"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" DROP COLUMN "deleted_at"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" ADD CONSTRAINT "FK_38179b299795e48887fc99f937a" FOREIGN KEY ("object_id") REFERENCES "metadata"."object_metadata"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" DROP CONSTRAINT "FK_38179b299795e48887fc99f937a"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" ADD "deleted_at" TIMESTAMP`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."object_metadata" ADD "deleted_at" TIMESTAMP`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."field_metadata" ADD CONSTRAINT "FK_38179b299795e48887fc99f937a" FOREIGN KEY ("object_id") REFERENCES "metadata"."object_metadata"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - } -} diff --git a/server/src/metadata/migrations/1699289664146-addRelationMetadata.ts b/server/src/metadata/migrations/1699289664146-addRelationMetadata.ts deleted file mode 100644 index f2b7d333b43..00000000000 --- a/server/src/metadata/migrations/1699289664146-addRelationMetadata.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddRelationMetadata1699289664146 implements MigrationInterface { - name = 'AddRelationMetadata1699289664146'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE TABLE "metadata"."relationMetadata" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "relationType" character varying NOT NULL, "fromObjectMetadataId" uuid NOT NULL, "toObjectMetadataId" uuid NOT NULL, "fromFieldMetadataId" uuid NOT NULL, "toFieldMetadataId" uuid NOT NULL, "workspaceId" character varying NOT NULL, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "REL_3deb257254145a3bdde9575e7d" UNIQUE ("fromFieldMetadataId"), CONSTRAINT "REL_9dea8f90d04edbbf9c541a95c3" UNIQUE ("toFieldMetadataId"), CONSTRAINT "PK_2724f60cb4f17a89481a7e8d7d3" PRIMARY KEY ("id"))`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."relationMetadata" ADD CONSTRAINT "FK_f2a0acd3a548ee446a1a35df44d" FOREIGN KEY ("fromObjectMetadataId") REFERENCES "metadata"."object_metadata"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."relationMetadata" ADD CONSTRAINT "FK_0f781f589e5a527b8f3d3a4b824" FOREIGN KEY ("toObjectMetadataId") REFERENCES "metadata"."object_metadata"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."relationMetadata" ADD CONSTRAINT "FK_3deb257254145a3bdde9575e7d6" FOREIGN KEY ("fromFieldMetadataId") REFERENCES "metadata"."field_metadata"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."relationMetadata" ADD CONSTRAINT "FK_9dea8f90d04edbbf9c541a95c3b" FOREIGN KEY ("toFieldMetadataId") REFERENCES "metadata"."field_metadata"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "metadata"."relationMetadata" DROP CONSTRAINT "FK_9dea8f90d04edbbf9c541a95c3b"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."relationMetadata" DROP CONSTRAINT "FK_3deb257254145a3bdde9575e7d6"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."relationMetadata" DROP CONSTRAINT "FK_0f781f589e5a527b8f3d3a4b824"`, - ); - await queryRunner.query( - `ALTER TABLE "metadata"."relationMetadata" DROP CONSTRAINT "FK_f2a0acd3a548ee446a1a35df44d"`, - ); - await queryRunner.query(`DROP TABLE "metadata"."relationMetadata"`); - } -} diff --git a/server/src/metadata/object-metadata/dtos/create-object.input.ts b/server/src/metadata/object-metadata/dtos/create-object.input.ts index 4c0ac158ed7..505bb7a609f 100644 --- a/server/src/metadata/object-metadata/dtos/create-object.input.ts +++ b/server/src/metadata/object-metadata/dtos/create-object.input.ts @@ -1,8 +1,12 @@ -import { Field, InputType } from '@nestjs/graphql'; +import { Field, HideField, InputType } from '@nestjs/graphql'; +import { BeforeCreateOne } from '@ptc-org/nestjs-query-graphql'; import { IsNotEmpty, IsOptional, IsString } from 'class-validator'; +import { BeforeCreateOneObject } from 'src/metadata/object-metadata/hooks/before-create-one-object.hook'; + @InputType() +@BeforeCreateOne(BeforeCreateOneObject) export class CreateObjectInput { @IsString() @IsNotEmpty() @@ -33,4 +37,10 @@ export class CreateObjectInput { @IsOptional() @Field({ nullable: true }) icon?: string; + + @HideField() + dataSourceId: string; + + @HideField() + workspaceId: string; } diff --git a/server/src/metadata/object-metadata/dtos/object-metadata.dto.ts b/server/src/metadata/object-metadata/dtos/object-metadata.dto.ts new file mode 100644 index 00000000000..5baaec569ee --- /dev/null +++ b/server/src/metadata/object-metadata/dtos/object-metadata.dto.ts @@ -0,0 +1,64 @@ +import { ObjectType, ID, Field, HideField } from '@nestjs/graphql'; + +import { + Authorize, + CursorConnection, + IDField, + QueryOptions, +} from '@ptc-org/nestjs-query-graphql'; + +import { FieldMetadataDTO } from 'src/metadata/field-metadata/dtos/field-metadata.dto'; + +@ObjectType('object') +@Authorize({ + authorize: (context: any) => ({ + workspaceId: { eq: context?.req?.user?.workspace?.id }, + }), +}) +@QueryOptions({ + defaultResultSize: 10, + disableFilter: true, + disableSort: true, + maxResultsSize: 1000, +}) +@CursorConnection('fields', () => FieldMetadataDTO) +export class ObjectMetadataDTO { + @IDField(() => ID) + id: string; + + @Field() + dataSourceId: string; + + @Field() + nameSingular: string; + + @Field() + namePlural: string; + + @Field() + labelSingular: string; + + @Field() + labelPlural: string; + + @Field({ nullable: true }) + description: string; + + @Field({ nullable: true }) + icon: string; + + @Field() + isCustom: boolean; + + @Field() + isActive: boolean; + + @HideField() + workspaceId: string; + + @Field() + createdAt: Date; + + @Field() + updatedAt: Date; +} diff --git a/server/src/metadata/object-metadata/hooks/before-create-one-object.hook.ts b/server/src/metadata/object-metadata/hooks/before-create-one-object.hook.ts index b291d2bbd14..5e2332dc127 100644 --- a/server/src/metadata/object-metadata/hooks/before-create-one-object.hook.ts +++ b/server/src/metadata/object-metadata/hooks/before-create-one-object.hook.ts @@ -5,14 +5,14 @@ import { CreateOneInputType, } from '@ptc-org/nestjs-query-graphql'; -import { DataSourceMetadataService } from 'src/metadata/data-source-metadata/data-source-metadata.service'; -import { ObjectMetadata } from 'src/metadata/object-metadata/object-metadata.entity'; +import { DataSourceService } from 'src/metadata/data-source/data-source.service'; +import { CreateObjectInput } from 'src/metadata/object-metadata/dtos/create-object.input'; @Injectable() -export class BeforeCreateOneObject +export class BeforeCreateOneObject implements BeforeCreateOneHook { - constructor(readonly dataSourceMetadataService: DataSourceMetadataService) {} + constructor(readonly dataSourceService: DataSourceService) {} async run( instance: CreateOneInputType, @@ -25,15 +25,12 @@ export class BeforeCreateOneObject } const lastDataSourceMetadata = - await this.dataSourceMetadataService.getLastDataSourceMetadataFromWorkspaceIdOrFail( + await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail( workspaceId, ); instance.input.dataSourceId = lastDataSourceMetadata.id; - instance.input.targetTableName = `_${instance.input.namePlural}`; instance.input.workspaceId = workspaceId; - instance.input.isActive = true; - instance.input.isCustom = true; return instance; } } diff --git a/server/src/metadata/object-metadata/object-metadata.auto-resolver-opts.ts b/server/src/metadata/object-metadata/object-metadata.auto-resolver-opts.ts deleted file mode 100644 index dd0477c50ec..00000000000 --- a/server/src/metadata/object-metadata/object-metadata.auto-resolver-opts.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { SortDirection } from '@ptc-org/nestjs-query-core'; -import { - AutoResolverOpts, - PagingStrategies, - ReadResolverOpts, -} from '@ptc-org/nestjs-query-graphql'; - -import { JwtAuthGuard } from 'src/guards/jwt.auth.guard'; - -import { ObjectMetadata } from './object-metadata.entity'; - -import { ObjectMetadataService } from './services/object-metadata.service'; -import { CreateObjectInput } from './dtos/create-object.input'; -import { UpdateObjectInput } from './dtos/update-object.input'; - -export const objectMetadataAutoResolverOpts: AutoResolverOpts< - any, - any, - unknown, - unknown, - ReadResolverOpts, - PagingStrategies ->[] = [ - { - EntityClass: ObjectMetadata, - DTOClass: ObjectMetadata, - CreateDTOClass: CreateObjectInput, - UpdateDTOClass: UpdateObjectInput, - ServiceClass: ObjectMetadataService, - enableTotalCount: true, - pagingStrategy: PagingStrategies.CURSOR, - read: { - defaultSort: [{ field: 'id', direction: SortDirection.DESC }], - }, - create: { - many: { disabled: true }, - }, - update: { - many: { disabled: true }, - }, - delete: { many: { disabled: true } }, - guards: [JwtAuthGuard], - }, -]; diff --git a/server/src/metadata/object-metadata/object-metadata.entity.ts b/server/src/metadata/object-metadata/object-metadata.entity.ts index 4caabefb133..8eda3ecfd64 100644 --- a/server/src/metadata/object-metadata/object-metadata.entity.ts +++ b/server/src/metadata/object-metadata/object-metadata.entity.ts @@ -1,112 +1,81 @@ -import { ObjectType, ID, Field } from '@nestjs/graphql'; - import { - Column, - CreateDateColumn, Entity, - OneToMany, - PrimaryGeneratedColumn, Unique, + PrimaryGeneratedColumn, + Column, + OneToMany, + CreateDateColumn, UpdateDateColumn, } from 'typeorm'; -import { - Authorize, - BeforeCreateOne, - CursorConnection, - IDField, - QueryOptions, -} from '@ptc-org/nestjs-query-graphql'; import { ObjectMetadataInterface } from 'src/tenant/schema-builder/interfaces/object-metadata.interface'; -import { FieldMetadata } from 'src/metadata/field-metadata/field-metadata.entity'; -import { RelationMetadata } from 'src/metadata/relation-metadata/relation-metadata.entity'; +import { FieldMetadataEntity } from 'src/metadata/field-metadata/field-metadata.entity'; +import { RelationMetadataEntity } from 'src/metadata/relation-metadata/relation-metadata.entity'; -import { BeforeCreateOneObject } from './hooks/before-create-one-object.hook'; - -@Entity('object_metadata') -@ObjectType('object') -@BeforeCreateOne(BeforeCreateOneObject) -@Authorize({ - authorize: (context: any) => ({ - workspaceId: { eq: context?.req?.user?.workspace?.id }, - }), -}) -@QueryOptions({ - defaultResultSize: 10, - disableFilter: true, - disableSort: true, - maxResultsSize: 1000, -}) -@CursorConnection('fields', () => FieldMetadata) +@Entity('objectMetadata') @Unique('IndexOnNameSingularAndWorkspaceIdUnique', [ 'nameSingular', 'workspaceId', ]) @Unique('IndexOnNamePluralAndWorkspaceIdUnique', ['namePlural', 'workspaceId']) -export class ObjectMetadata implements ObjectMetadataInterface { - @IDField(() => ID) +export class ObjectMetadataEntity implements ObjectMetadataInterface { @PrimaryGeneratedColumn('uuid') id: string; - @Field() - @Column({ nullable: false, name: 'data_source_id' }) + @Column({ nullable: false, type: 'uuid' }) dataSourceId: string; - @Field() - @Column({ nullable: false, name: 'name_singular' }) + @Column({ nullable: false }) nameSingular: string; - @Field() - @Column({ nullable: false, name: 'name_plural' }) + @Column({ nullable: false }) namePlural: string; - @Field() - @Column({ nullable: false, name: 'label_singular' }) + @Column({ nullable: false }) labelSingular: string; - @Field() - @Column({ nullable: false, name: 'label_plural' }) + @Column({ nullable: false }) labelPlural: string; - @Field({ nullable: true }) - @Column({ nullable: true, name: 'description', type: 'text' }) + @Column({ nullable: true, type: 'text' }) description: string; - @Field({ nullable: true }) - @Column({ nullable: true, name: 'icon' }) + @Column({ nullable: true }) icon: string; - @Column({ nullable: false, name: 'target_table_name' }) + @Column({ nullable: false }) targetTableName: string; - @Field() - @Column({ default: false, name: 'is_custom' }) + @Column({ default: false }) isCustom: boolean; - @Field() - @Column({ default: false, name: 'is_active' }) + @Column({ default: false }) isActive: boolean; - @Column({ nullable: false, name: 'workspace_id' }) + @Column({ nullable: false }) workspaceId: string; - @OneToMany(() => FieldMetadata, (field) => field.object, { + @OneToMany(() => FieldMetadataEntity, (field) => field.object, { cascade: true, }) - fields: FieldMetadata[]; + fields: FieldMetadataEntity[]; - @OneToMany(() => RelationMetadata, (relation) => relation.fromObjectMetadata) - fromRelations: RelationMetadata[]; + @OneToMany( + () => RelationMetadataEntity, + (relation: RelationMetadataEntity) => relation.fromObjectMetadata, + ) + fromRelations: RelationMetadataEntity[]; - @OneToMany(() => RelationMetadata, (relation) => relation.toObjectMetadata) - toRelations: RelationMetadata[]; + @OneToMany( + () => RelationMetadataEntity, + (relation: RelationMetadataEntity) => relation.toObjectMetadata, + ) + toRelations: RelationMetadataEntity[]; - @Field() - @CreateDateColumn({ name: 'created_at' }) + @CreateDateColumn() createdAt: Date; - @Field() - @UpdateDateColumn({ name: 'updated_at' }) + @UpdateDateColumn() updatedAt: Date; } diff --git a/server/src/metadata/object-metadata/object-metadata.module.ts b/server/src/metadata/object-metadata/object-metadata.module.ts index 24822972aab..fb281e297f2 100644 --- a/server/src/metadata/object-metadata/object-metadata.module.ts +++ b/server/src/metadata/object-metadata/object-metadata.module.ts @@ -1,28 +1,56 @@ import { Module } from '@nestjs/common'; -import { NestjsQueryGraphQLModule } from '@ptc-org/nestjs-query-graphql'; +import { + NestjsQueryGraphQLModule, + PagingStrategies, +} from '@ptc-org/nestjs-query-graphql'; import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm'; +import { SortDirection } from '@ptc-org/nestjs-query-core'; -import { DataSourceMetadataModule } from 'src/metadata/data-source-metadata/data-source-metadata.module'; -import { MigrationRunnerModule } from 'src/metadata/migration-runner/migration-runner.module'; +import { DataSourceModule } from 'src/metadata/data-source/data-source.module'; +import { TenantMigrationRunnerModule } from 'src/tenant-migration-runner/tenant-migration-runner.module'; import { TenantMigrationModule } from 'src/metadata/tenant-migration/tenant-migration.module'; +import { JwtAuthGuard } from 'src/guards/jwt.auth.guard'; -import { ObjectMetadata } from './object-metadata.entity'; -import { objectMetadataAutoResolverOpts } from './object-metadata.auto-resolver-opts'; +import { ObjectMetadataService } from './object-metadata.service'; +import { ObjectMetadataEntity } from './object-metadata.entity'; -import { ObjectMetadataService } from './services/object-metadata.service'; +import { CreateObjectInput } from './dtos/create-object.input'; +import { UpdateObjectInput } from './dtos/update-object.input'; +import { ObjectMetadataDTO } from './dtos/object-metadata.dto'; @Module({ imports: [ NestjsQueryGraphQLModule.forFeature({ imports: [ - NestjsQueryTypeOrmModule.forFeature([ObjectMetadata], 'metadata'), - DataSourceMetadataModule, + NestjsQueryTypeOrmModule.forFeature([ObjectMetadataEntity], 'metadata'), + DataSourceModule, TenantMigrationModule, - MigrationRunnerModule, + TenantMigrationRunnerModule, ], services: [ObjectMetadataService], - resolvers: objectMetadataAutoResolverOpts, + resolvers: [ + { + EntityClass: ObjectMetadataEntity, + DTOClass: ObjectMetadataDTO, + CreateDTOClass: CreateObjectInput, + UpdateDTOClass: UpdateObjectInput, + ServiceClass: ObjectMetadataService, + enableTotalCount: true, + pagingStrategy: PagingStrategies.CURSOR, + read: { + defaultSort: [{ field: 'id', direction: SortDirection.DESC }], + }, + create: { + many: { disabled: true }, + }, + update: { + many: { disabled: true }, + }, + delete: { many: { disabled: true } }, + guards: [JwtAuthGuard], + }, + ], }), ], providers: [ObjectMetadataService], diff --git a/server/src/metadata/object-metadata/services/object-metadata.service.ts b/server/src/metadata/object-metadata/object-metadata.service.ts similarity index 68% rename from server/src/metadata/object-metadata/services/object-metadata.service.ts rename to server/src/metadata/object-metadata/object-metadata.service.ts index bcc53fc668f..52cec11b7c5 100644 --- a/server/src/metadata/object-metadata/services/object-metadata.service.ts +++ b/server/src/metadata/object-metadata/object-metadata.service.ts @@ -7,30 +7,28 @@ import { InjectRepository } from '@nestjs/typeorm'; import { Equal, In, Repository } from 'typeorm'; import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm'; -import { DeleteOneOptions } from '@ptc-org/nestjs-query-core'; import { TenantMigrationService } from 'src/metadata/tenant-migration/tenant-migration.service'; +import { TenantMigrationRunnerService } from 'src/tenant-migration-runner/tenant-migration-runner.service'; import { TenantMigrationTableAction } from 'src/metadata/tenant-migration/tenant-migration.entity'; -import { MigrationRunnerService } from 'src/metadata/migration-runner/migration-runner.service'; -import { ObjectMetadata } from 'src/metadata/object-metadata/object-metadata.entity'; -import { standardObjectsMetadata } from 'src/metadata/standard-objects/standard-object-metadata'; + +import { ObjectMetadataEntity } from './object-metadata.entity'; + +import { CreateObjectInput } from './dtos/create-object.input'; @Injectable() -export class ObjectMetadataService extends TypeOrmQueryService { +export class ObjectMetadataService extends TypeOrmQueryService { constructor( - @InjectRepository(ObjectMetadata, 'metadata') - private readonly objectMetadataRepository: Repository, + @InjectRepository(ObjectMetadataEntity, 'metadata') + private readonly objectMetadataRepository: Repository, private readonly tenantMigrationService: TenantMigrationService, - private readonly migrationRunnerService: MigrationRunnerService, + private readonly migrationRunnerService: TenantMigrationRunnerService, ) { super(objectMetadataRepository); } - override async deleteOne( - id: string, - opts?: DeleteOneOptions | undefined, - ): Promise { + override async deleteOne(id: string): Promise { const objectMetadata = await this.objectMetadataRepository.findOne({ where: { id }, }); @@ -47,11 +45,18 @@ export class ObjectMetadataService extends TypeOrmQueryService { throw new BadRequestException("Active objects can't be deleted"); } - return super.deleteOne(id, opts); + return super.deleteOne(id); } - override async createOne(record: ObjectMetadata): Promise { - const createdObjectMetadata = await super.createOne(record); + override async createOne( + record: CreateObjectInput, + ): Promise { + const createdObjectMetadata = await super.createOne({ + ...record, + targetTableName: `_${record.nameSingular}`, + isActive: true, + isCustom: true, + }); await this.tenantMigrationService.createCustomMigration( createdObjectMetadata.workspaceId, @@ -121,34 +126,6 @@ export class ObjectMetadataService extends TypeOrmQueryService { }); } - /** - * - * Create all standard objects and fields metadata for a given workspace - * - * @param dataSourceMetadataId - * @param workspaceId - */ - public async createStandardObjectsAndFieldsMetadata( - dataSourceMetadataId: string, - workspaceId: string, - ) { - await this.objectMetadataRepository.save( - Object.values(standardObjectsMetadata).map((objectMetadata) => ({ - ...objectMetadata, - dataSourceId: dataSourceMetadataId, - workspaceId, - isCustom: false, - isActive: true, - fields: objectMetadata.fields.map((field) => ({ - ...field, - workspaceId, - isCustom: false, - isActive: true, - })), - })), - ); - } - public async deleteObjectsMetadata(workspaceId: string) { await this.objectMetadataRepository.delete({ workspaceId }); } diff --git a/server/src/metadata/object-metadata/services/object-metadata.service.spec.ts b/server/src/metadata/object-metadata/services/object-metadata.service.spec.ts deleted file mode 100644 index 8b179110b3e..00000000000 --- a/server/src/metadata/object-metadata/services/object-metadata.service.spec.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { getRepositoryToken } from '@nestjs/typeorm'; - -import { ObjectMetadata } from 'src/metadata/object-metadata/object-metadata.entity'; -import { MigrationRunnerService } from 'src/metadata/migration-runner/migration-runner.service'; -import { TenantMigrationService } from 'src/metadata/tenant-migration/tenant-migration.service'; - -import { ObjectMetadataService } from './object-metadata.service'; - -describe('ObjectMetadataService', () => { - let service: ObjectMetadataService; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - ObjectMetadataService, - { - provide: getRepositoryToken(ObjectMetadata, 'metadata'), - useValue: {}, - }, - { - provide: TenantMigrationService, - useValue: {}, - }, - { - provide: MigrationRunnerService, - useValue: {}, - }, - ], - }).compile(); - - service = module.get(ObjectMetadataService); - }); - - it('should be defined', () => { - expect(service).toBeDefined(); - }); -}); diff --git a/server/src/metadata/relation-metadata/dtos/create-relation.input.ts b/server/src/metadata/relation-metadata/dtos/create-relation.input.ts index b7ee42b2f8a..31cd26d31b8 100644 --- a/server/src/metadata/relation-metadata/dtos/create-relation.input.ts +++ b/server/src/metadata/relation-metadata/dtos/create-relation.input.ts @@ -1,4 +1,4 @@ -import { Field, InputType } from '@nestjs/graphql'; +import { Field, HideField, InputType } from '@nestjs/graphql'; import { BeforeCreateOne } from '@ptc-org/nestjs-query-graphql'; import { @@ -9,8 +9,8 @@ import { IsUUID, } from 'class-validator'; -import { RelationMetadataType } from 'src/metadata/relation-metadata/relation-metadata.entity'; import { BeforeCreateOneRelation } from 'src/metadata/relation-metadata/hooks/before-create-one-relation.hook'; +import { RelationMetadataType } from 'src/metadata/relation-metadata/relation-metadata.entity'; @InputType() @BeforeCreateOne(BeforeCreateOneRelation) @@ -50,5 +50,6 @@ export class CreateRelationInput { @Field({ nullable: true }) icon?: string; + @HideField() workspaceId: string; } diff --git a/server/src/metadata/relation-metadata/dtos/relation-metadata.dto.ts b/server/src/metadata/relation-metadata/dtos/relation-metadata.dto.ts new file mode 100644 index 00000000000..348d0ab3b38 --- /dev/null +++ b/server/src/metadata/relation-metadata/dtos/relation-metadata.dto.ts @@ -0,0 +1,57 @@ +import { ObjectType, ID, Field, HideField } from '@nestjs/graphql'; + +import { CreateDateColumn, UpdateDateColumn } from 'typeorm'; +import { + Authorize, + IDField, + QueryOptions, + Relation, +} from '@ptc-org/nestjs-query-graphql'; + +import { ObjectMetadataDTO } from 'src/metadata/object-metadata/dtos/object-metadata.dto'; +import { RelationMetadataType } from 'src/metadata/relation-metadata/relation-metadata.entity'; + +@ObjectType('relation') +@Authorize({ + authorize: (context: any) => ({ + workspaceId: { eq: context?.req?.user?.workspace?.id }, + }), +}) +@QueryOptions({ + defaultResultSize: 10, + disableFilter: true, + disableSort: true, + maxResultsSize: 1000, +}) +@Relation('fromObjectMetadata', () => ObjectMetadataDTO) +@Relation('toObjectMetadata', () => ObjectMetadataDTO) +export class RelationMetadataDTO { + @IDField(() => ID) + id: string; + + @Field() + relationType: RelationMetadataType; + + @Field() + fromObjectMetadataId: string; + + @Field() + toObjectMetadataId: string; + + @Field() + fromFieldMetadataId: string; + + @Field() + toFieldMetadataId: string; + + @HideField() + workspaceId: string; + + @Field() + @CreateDateColumn() + createdAt: Date; + + @Field() + @UpdateDateColumn() + updatedAt: Date; +} diff --git a/server/src/metadata/relation-metadata/relation-metadata.auto-resolver-opts.ts b/server/src/metadata/relation-metadata/relation-metadata.auto-resolver-opts.ts deleted file mode 100644 index c9e902321af..00000000000 --- a/server/src/metadata/relation-metadata/relation-metadata.auto-resolver-opts.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { - AutoResolverOpts, - PagingStrategies, - ReadResolverOpts, -} from '@ptc-org/nestjs-query-graphql'; - -import { JwtAuthGuard } from 'src/guards/jwt.auth.guard'; - -import { RelationMetadata } from './relation-metadata.entity'; - -import { RelationMetadataService } from './services/relation-metadata.service'; -import { CreateRelationInput } from './dtos/create-relation.input'; - -export const relationMetadataAutoResolverOpts: AutoResolverOpts< - any, - any, - unknown, - unknown, - ReadResolverOpts, - PagingStrategies ->[] = [ - { - EntityClass: RelationMetadata, - DTOClass: RelationMetadata, - ServiceClass: RelationMetadataService, - CreateDTOClass: CreateRelationInput, - enableTotalCount: true, - pagingStrategy: PagingStrategies.CURSOR, - read: { many: { disabled: true } }, - create: { many: { disabled: true } }, - update: { disabled: true }, - delete: { disabled: true }, - guards: [JwtAuthGuard], - }, -]; diff --git a/server/src/metadata/relation-metadata/relation-metadata.entity.ts b/server/src/metadata/relation-metadata/relation-metadata.entity.ts index fa0b1a93ccf..95c2c568730 100644 --- a/server/src/metadata/relation-metadata/relation-metadata.entity.ts +++ b/server/src/metadata/relation-metadata/relation-metadata.entity.ts @@ -1,5 +1,3 @@ -import { ObjectType, ID, Field } from '@nestjs/graphql'; - import { Column, CreateDateColumn, @@ -10,17 +8,11 @@ import { PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; -import { - Authorize, - IDField, - QueryOptions, - Relation, -} from '@ptc-org/nestjs-query-graphql'; import { RelationMetadataInterface } from 'src/tenant/schema-builder/interfaces/relation-metadata.interface'; -import { FieldMetadata } from 'src/metadata/field-metadata/field-metadata.entity'; -import { ObjectMetadata } from 'src/metadata/object-metadata/object-metadata.entity'; +import { FieldMetadataEntity } from 'src/metadata/field-metadata/field-metadata.entity'; +import { ObjectMetadataEntity } from 'src/metadata/object-metadata/object-metadata.entity'; export enum RelationMetadataType { ONE_TO_ONE = 'ONE_TO_ONE', @@ -29,67 +21,57 @@ export enum RelationMetadataType { } @Entity('relationMetadata') -@ObjectType('relation') -@Authorize({ - authorize: (context: any) => ({ - workspaceId: { eq: context?.req?.user?.workspace?.id }, - }), -}) -@QueryOptions({ - defaultResultSize: 10, - disableFilter: true, - disableSort: true, - maxResultsSize: 1000, -}) -@Relation('fromObjectMetadata', () => ObjectMetadata) -@Relation('toObjectMetadata', () => ObjectMetadata) -export class RelationMetadata implements RelationMetadataInterface { - @IDField(() => ID) +export class RelationMetadataEntity implements RelationMetadataInterface { @PrimaryGeneratedColumn('uuid') id: string; - @Field() @Column({ nullable: false }) relationType: RelationMetadataType; - @Field() @Column({ nullable: false, type: 'uuid' }) fromObjectMetadataId: string; - @Field() @Column({ nullable: false, type: 'uuid' }) toObjectMetadataId: string; - @Field() @Column({ nullable: false, type: 'uuid' }) fromFieldMetadataId: string; - @Field() @Column({ nullable: false, type: 'uuid' }) toFieldMetadataId: string; @Column({ nullable: false }) workspaceId: string; - @ManyToOne(() => ObjectMetadata, (object) => object.fromRelations) - fromObjectMetadata: ObjectMetadata; + @ManyToOne( + () => ObjectMetadataEntity, + (object: ObjectMetadataEntity) => object.fromRelations, + ) + fromObjectMetadata: ObjectMetadataEntity; - @ManyToOne(() => ObjectMetadata, (object) => object.toRelations) - toObjectMetadata: ObjectMetadata; + @ManyToOne( + () => ObjectMetadataEntity, + (object: ObjectMetadataEntity) => object.toRelations, + ) + toObjectMetadata: ObjectMetadataEntity; - @OneToOne(() => FieldMetadata, (field) => field.fromRelationMetadata) + @OneToOne( + () => FieldMetadataEntity, + (field: FieldMetadataEntity) => field.fromRelationMetadata, + ) @JoinColumn() - fromFieldMetadata: FieldMetadata; + fromFieldMetadata: FieldMetadataEntity; - @OneToOne(() => FieldMetadata, (field) => field.toRelationMetadata) + @OneToOne( + () => FieldMetadataEntity, + (field: FieldMetadataEntity) => field.toRelationMetadata, + ) @JoinColumn() - toFieldMetadata: FieldMetadata; + toFieldMetadata: FieldMetadataEntity; - @Field() @CreateDateColumn() createdAt: Date; - @Field() @UpdateDateColumn() updatedAt: Date; } diff --git a/server/src/metadata/relation-metadata/relation-metadata.module.ts b/server/src/metadata/relation-metadata/relation-metadata.module.ts index 1900be0ade2..9ef0ea6021f 100644 --- a/server/src/metadata/relation-metadata/relation-metadata.module.ts +++ b/server/src/metadata/relation-metadata/relation-metadata.module.ts @@ -1,30 +1,52 @@ import { Module } from '@nestjs/common'; -import { NestjsQueryGraphQLModule } from '@ptc-org/nestjs-query-graphql'; +import { + NestjsQueryGraphQLModule, + PagingStrategies, +} from '@ptc-org/nestjs-query-graphql'; import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm'; import { FieldMetadataModule } from 'src/metadata/field-metadata/field-metadata.module'; import { ObjectMetadataModule } from 'src/metadata/object-metadata/object-metadata.module'; -import { MigrationRunnerModule } from 'src/metadata/migration-runner/migration-runner.module'; +import { TenantMigrationRunnerModule } from 'src/tenant-migration-runner/tenant-migration-runner.module'; import { TenantMigrationModule } from 'src/metadata/tenant-migration/tenant-migration.module'; +import { JwtAuthGuard } from 'src/guards/jwt.auth.guard'; -import { RelationMetadata } from './relation-metadata.entity'; -import { relationMetadataAutoResolverOpts } from './relation-metadata.auto-resolver-opts'; +import { RelationMetadataService } from './relation-metadata.service'; +import { RelationMetadataEntity } from './relation-metadata.entity'; -import { RelationMetadataService } from './services/relation-metadata.service'; +import { CreateRelationInput } from './dtos/create-relation.input'; +import { RelationMetadataDTO } from './dtos/relation-metadata.dto'; @Module({ imports: [ NestjsQueryGraphQLModule.forFeature({ imports: [ - NestjsQueryTypeOrmModule.forFeature([RelationMetadata], 'metadata'), + NestjsQueryTypeOrmModule.forFeature( + [RelationMetadataEntity], + 'metadata', + ), ObjectMetadataModule, FieldMetadataModule, - MigrationRunnerModule, + TenantMigrationRunnerModule, TenantMigrationModule, ], services: [RelationMetadataService], - resolvers: relationMetadataAutoResolverOpts, + resolvers: [ + { + EntityClass: RelationMetadataEntity, + DTOClass: RelationMetadataDTO, + ServiceClass: RelationMetadataService, + CreateDTOClass: CreateRelationInput, + enableTotalCount: true, + pagingStrategy: PagingStrategies.CURSOR, + read: { many: { disabled: true } }, + create: { many: { disabled: true } }, + update: { disabled: true }, + delete: { disabled: true }, + guards: [JwtAuthGuard], + }, + ], }), ], providers: [RelationMetadataService], diff --git a/server/src/metadata/relation-metadata/services/relation-metadata.service.ts b/server/src/metadata/relation-metadata/relation-metadata.service.ts similarity index 88% rename from server/src/metadata/relation-metadata/services/relation-metadata.service.ts rename to server/src/metadata/relation-metadata/relation-metadata.service.ts index 4375dc1a1df..0ef71eb2069 100644 --- a/server/src/metadata/relation-metadata/services/relation-metadata.service.ts +++ b/server/src/metadata/relation-metadata/relation-metadata.service.ts @@ -8,34 +8,35 @@ import { InjectRepository } from '@nestjs/typeorm'; import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm'; import { Repository } from 'typeorm'; -import { - RelationMetadata, - RelationMetadataType, -} from 'src/metadata/relation-metadata/relation-metadata.entity'; -import { ObjectMetadataService } from 'src/metadata/object-metadata/services/object-metadata.service'; -import { FieldMetadataService } from 'src/metadata/field-metadata/services/field-metadata.service'; -import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity'; +import { ObjectMetadataService } from 'src/metadata/object-metadata/object-metadata.service'; +import { FieldMetadataService } from 'src/metadata/field-metadata/field-metadata.service'; import { CreateRelationInput } from 'src/metadata/relation-metadata/dtos/create-relation.input'; -import { MigrationRunnerService } from 'src/metadata/migration-runner/migration-runner.service'; +import { TenantMigrationRunnerService } from 'src/tenant-migration-runner/tenant-migration-runner.service'; import { TenantMigrationService } from 'src/metadata/tenant-migration/tenant-migration.service'; +import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity'; import { TenantMigrationColumnActionType } from 'src/metadata/tenant-migration/tenant-migration.entity'; +import { + RelationMetadataEntity, + RelationMetadataType, +} from './relation-metadata.entity'; + @Injectable() -export class RelationMetadataService extends TypeOrmQueryService { +export class RelationMetadataService extends TypeOrmQueryService { constructor( - @InjectRepository(RelationMetadata, 'metadata') - private readonly relationMetadataRepository: Repository, + @InjectRepository(RelationMetadataEntity, 'metadata') + private readonly relationMetadataRepository: Repository, private readonly objectMetadataService: ObjectMetadataService, private readonly fieldMetadataService: FieldMetadataService, private readonly tenantMigrationService: TenantMigrationService, - private readonly migrationRunnerService: MigrationRunnerService, + private readonly migrationRunnerService: TenantMigrationRunnerService, ) { super(relationMetadataRepository); } override async createOne( record: CreateRelationInput, - ): Promise { + ): Promise { if (record.relationType === RelationMetadataType.MANY_TO_MANY) { throw new BadRequestException( 'Many to many relations are not supported yet', @@ -72,7 +73,7 @@ export class RelationMetadataService extends TypeOrmQueryService { - acc[curr.objectId] = curr; + acc[curr.objectMetadataId] = curr; return acc; }, {}); diff --git a/server/src/metadata/tenant-initialisation/tenant-initialisation.service.ts b/server/src/metadata/tenant-initialisation/tenant-initialisation.service.ts deleted file mode 100644 index 31154e52bd8..00000000000 --- a/server/src/metadata/tenant-initialisation/tenant-initialisation.service.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { Injectable } from '@nestjs/common'; - -import { TenantMigrationService } from 'src/metadata/tenant-migration/tenant-migration.service'; -import { MigrationRunnerService } from 'src/metadata/migration-runner/migration-runner.service'; -import { DataSourceService } from 'src/metadata/data-source/data-source.service'; -import { DataSourceMetadataService } from 'src/metadata/data-source-metadata/data-source-metadata.service'; -import { ObjectMetadataService } from 'src/metadata/object-metadata/services/object-metadata.service'; -import { DataSourceMetadata } from 'src/metadata/data-source-metadata/data-source-metadata.entity'; -import { FieldMetadataService } from 'src/metadata/field-metadata/services/field-metadata.service'; - -import { standardObjectsPrefillData } from './standard-objects-prefill-data/standard-objects-prefill-data'; - -@Injectable() -export class TenantInitialisationService { - constructor( - private readonly dataSourceService: DataSourceService, - private readonly tenantMigrationService: TenantMigrationService, - private readonly migrationRunnerService: MigrationRunnerService, - private readonly objectMetadataService: ObjectMetadataService, - private readonly fieldMetadataService: FieldMetadataService, - private readonly dataSourceMetadataService: DataSourceMetadataService, - ) {} - - /** - * Init a workspace by creating a new data source and running all migrations - * @param workspaceId - * @returns Promise - */ - public async init(workspaceId: string): Promise { - const schemaName = await this.dataSourceService.createWorkspaceSchema( - workspaceId, - ); - - const dataSourceMetadata = - await this.dataSourceMetadataService.createDataSourceMetadata( - workspaceId, - schemaName, - ); - - await this.tenantMigrationService.insertStandardMigrations(workspaceId); - - // Todo: keep in mind that we don't handle concurrency issues such as migrations being created at the same time - // but it shouldn't be the role of this service to handle this kind of issues for now. - // To check later when we run this in a job. - await this.migrationRunnerService.executeMigrationFromPendingMigrations( - workspaceId, - ); - - await this.objectMetadataService.createStandardObjectsAndFieldsMetadata( - dataSourceMetadata.id, - workspaceId, - ); - - await this.prefillWorkspaceWithStandardObjects( - dataSourceMetadata, - workspaceId, - ); - } - - /** - * - * We are prefilling a few standard objects with data to make it easier for the user to get started. - * - * @param dataSourceMetadata - * @param workspaceId - */ - private async prefillWorkspaceWithStandardObjects( - dataSourceMetadata: DataSourceMetadata, - workspaceId: string, - ) { - const workspaceDataSource = - await this.dataSourceService.connectToWorkspaceDataSource(workspaceId); - - if (!workspaceDataSource) { - throw new Error('Could not connect to workspace data source'); - } - - standardObjectsPrefillData(workspaceDataSource, dataSourceMetadata.schema); - } - - public async delete(workspaceId: string): Promise { - // Delete data from metadata tables - await this.fieldMetadataService.deleteFieldsMetadata(workspaceId); - await this.objectMetadataService.deleteObjectsMetadata(workspaceId); - await this.tenantMigrationService.delete(workspaceId); - await this.dataSourceMetadataService.delete(workspaceId); - // Delete schema - await this.dataSourceService.deleteWorkspaceSchema(workspaceId); - } -} diff --git a/server/src/metadata/tenant-migration/tenant-migration.entity.ts b/server/src/metadata/tenant-migration/tenant-migration.entity.ts index de10716d374..110ba58bb28 100644 --- a/server/src/metadata/tenant-migration/tenant-migration.entity.ts +++ b/server/src/metadata/tenant-migration/tenant-migration.entity.ts @@ -32,8 +32,8 @@ export type TenantMigrationTableAction = { action: 'create' | 'alter'; columns?: TenantMigrationColumnAction[]; }; -@Entity('tenant_migrations') -export class TenantMigration { +@Entity('tenantMigration') +export class TenantMigrationEntity { @PrimaryGeneratedColumn('uuid') id: string; diff --git a/server/src/metadata/tenant-migration/tenant-migration.module.ts b/server/src/metadata/tenant-migration/tenant-migration.module.ts index 4bd73b39adb..035bfc8e6cc 100644 --- a/server/src/metadata/tenant-migration/tenant-migration.module.ts +++ b/server/src/metadata/tenant-migration/tenant-migration.module.ts @@ -2,10 +2,10 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { TenantMigrationService } from './tenant-migration.service'; -import { TenantMigration } from './tenant-migration.entity'; +import { TenantMigrationEntity } from './tenant-migration.entity'; @Module({ - imports: [TypeOrmModule.forFeature([TenantMigration], 'metadata')], + imports: [TypeOrmModule.forFeature([TenantMigrationEntity], 'metadata')], exports: [TenantMigrationService], providers: [TenantMigrationService], }) diff --git a/server/src/metadata/tenant-migration/tenant-migration.service.spec.ts b/server/src/metadata/tenant-migration/tenant-migration.service.spec.ts deleted file mode 100644 index 371fbdbadc7..00000000000 --- a/server/src/metadata/tenant-migration/tenant-migration.service.spec.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { getRepositoryToken } from '@nestjs/typeorm'; - -import { TenantMigrationService } from './tenant-migration.service'; -import { TenantMigration } from './tenant-migration.entity'; - -describe('TenantMigrationService', () => { - let service: TenantMigrationService; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - TenantMigrationService, - { - provide: getRepositoryToken(TenantMigration, 'metadata'), - useValue: {}, - }, - ], - }).compile(); - - service = module.get(TenantMigrationService); - }); - - it('should be defined', () => { - expect(service).toBeDefined(); - }); -}); diff --git a/server/src/metadata/tenant-migration/tenant-migration.service.ts b/server/src/metadata/tenant-migration/tenant-migration.service.ts index aa8af710aa3..62995c9cc14 100644 --- a/server/src/metadata/tenant-migration/tenant-migration.service.ts +++ b/server/src/metadata/tenant-migration/tenant-migration.service.ts @@ -3,17 +3,17 @@ import { InjectRepository } from '@nestjs/typeorm'; import { IsNull, Repository } from 'typeorm'; +import { standardMigrations } from './standard-migrations'; import { - TenantMigration, + TenantMigrationEntity, TenantMigrationTableAction, } from './tenant-migration.entity'; -import { standardMigrations } from './standard-migrations'; @Injectable() export class TenantMigrationService { constructor( - @InjectRepository(TenantMigration, 'metadata') - private readonly tenantMigrationRepository: Repository, + @InjectRepository(TenantMigrationEntity, 'metadata') + private readonly tenantMigrationRepository: Repository, ) {} /** @@ -60,7 +60,7 @@ export class TenantMigrationService { */ public async getPendingMigrations( workspaceId: string, - ): Promise { + ): Promise { return await this.tenantMigrationRepository.find({ order: { createdAt: 'ASC' }, where: { @@ -79,7 +79,7 @@ export class TenantMigrationService { */ public async setAppliedAtForMigration( workspaceId: string, - migration: TenantMigration, + migration: TenantMigrationEntity, ) { await this.tenantMigrationRepository.save({ id: migration.id, diff --git a/server/src/tenant-datasource/tenant-datasource.module.ts b/server/src/tenant-datasource/tenant-datasource.module.ts new file mode 100644 index 00000000000..5b6fff111ad --- /dev/null +++ b/server/src/tenant-datasource/tenant-datasource.module.ts @@ -0,0 +1,13 @@ +import { Module } from '@nestjs/common'; + +import { TypeORMModule } from 'src/database/typeorm/typeorm.module'; +import { DataSourceModule } from 'src/metadata/data-source/data-source.module'; + +import { TenantDataSourceService } from './tenant-datasource.service'; + +@Module({ + imports: [DataSourceModule, TypeORMModule], + exports: [TenantDataSourceService], + providers: [TenantDataSourceService], +}) +export class TenantDataSourceModule {} diff --git a/server/src/tenant-datasource/tenant-datasource.service.ts b/server/src/tenant-datasource/tenant-datasource.service.ts new file mode 100644 index 00000000000..6ba6e5e3319 --- /dev/null +++ b/server/src/tenant-datasource/tenant-datasource.service.ts @@ -0,0 +1,101 @@ +import { Injectable } from '@nestjs/common'; + +import { DataSource } from 'typeorm'; + +import { DataSourceService } from 'src/metadata/data-source/data-source.service'; +import { TypeORMService } from 'src/database/typeorm/typeorm.service'; + +@Injectable() +export class TenantDataSourceService { + constructor( + private readonly dataSourceService: DataSourceService, + private readonly typeormService: TypeORMService, + ) {} + + /** + * + * Connect to the workspace data source + * + * @param workspaceId + * @returns + */ + public async connectToWorkspaceDataSource( + workspaceId: string, + ): Promise { + const dataSourceMetadata = + await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail( + workspaceId, + ); + + const dataSource = await this.typeormService.connectToDataSource( + dataSourceMetadata, + ); + + if (!dataSource) { + throw new Error( + `Could not connect to workspace data source for workspace ${workspaceId}`, + ); + } + + return dataSource; + } + + /** + * + * Create a new DB schema for a workspace + * + * @param workspaceId + * @returns + */ + public async createWorkspaceDBSchema(workspaceId: string): Promise { + const schemaName = this.getSchemaName(workspaceId); + + return await this.typeormService.createSchema(schemaName); + } + + /** + * + * Delete a DB schema for a workspace + * + * @param workspaceId + * @returns + */ + public async deleteWorkspaceDBSchema(workspaceId: string): Promise { + const schemaName = this.getSchemaName(workspaceId); + + return await this.typeormService.deleteSchema(schemaName); + } + + /** + * + * Get the schema name for a workspace + * + * @param workspaceId + * @returns string + */ + public getSchemaName(workspaceId: string): string { + return `workspace_${this.uuidToBase36(workspaceId)}`; + } + + /** + * + * Convert a uuid to base36 + * + * @param uuid + * @returns string + */ + private uuidToBase36(uuid: string): string { + let devId = false; + + if (uuid.startsWith('twenty-')) { + devId = true; + // Clean dev uuids (twenty-) + uuid = uuid.replace('twenty-', ''); + } + const hexString = uuid.replace(/-/g, ''); + const base10Number = BigInt('0x' + hexString); + const base36String = base10Number.toString(36); + + return `${devId ? 'twenty_' : ''}${base36String}`; + } +} diff --git a/server/src/metadata/commands/sync-tenant-metadata.command.ts b/server/src/tenant-manager/commands/sync-tenant-metadata.command.ts similarity index 60% rename from server/src/metadata/commands/sync-tenant-metadata.command.ts rename to server/src/tenant-manager/commands/sync-tenant-metadata.command.ts index ffa299d275f..e0bdcdf8db4 100644 --- a/server/src/metadata/commands/sync-tenant-metadata.command.ts +++ b/server/src/tenant-manager/commands/sync-tenant-metadata.command.ts @@ -1,7 +1,7 @@ import { Command, CommandRunner, Option } from 'nest-commander'; -import { ObjectMetadataService } from 'src/metadata/object-metadata/services/object-metadata.service'; -import { DataSourceMetadataService } from 'src/metadata/data-source-metadata/data-source-metadata.service'; +import { DataSourceService } from 'src/metadata/data-source/data-source.service'; +import { TenantManagerService } from 'src/tenant-manager/tenant-manager.service'; // TODO: implement dry-run interface RunTenantMigrationsOptions { @@ -14,8 +14,8 @@ interface RunTenantMigrationsOptions { }) export class SyncTenantMetadataCommand extends CommandRunner { constructor( - private readonly objectMetadataService: ObjectMetadataService, - private readonly dataSourceMetadataService: DataSourceMetadataService, + private readonly tenantManagerService: TenantManagerService, + private readonly dataSourceService: DataSourceService, ) { super(); } @@ -26,16 +26,12 @@ export class SyncTenantMetadataCommand extends CommandRunner { ): Promise { // TODO: run in a dedicated job + run queries in a transaction. const dataSourceMetadata = - await this.dataSourceMetadataService.getLastDataSourceMetadataFromWorkspaceIdOrFail( + await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail( options.workspaceId, ); // TODO: This solution could be improved, using a diff for example, we should not have to delete all metadata and recreate them. - await this.objectMetadataService.deleteMany({ - workspaceId: { eq: options.workspaceId }, - }); - - await this.objectMetadataService.createStandardObjectsAndFieldsMetadata( + await this.tenantManagerService.resetStandardObjectsAndFieldsMetadata( dataSourceMetadata.id, options.workspaceId, ); diff --git a/server/src/tenant-manager/commands/tenant-manager-commands.module.ts b/server/src/tenant-manager/commands/tenant-manager-commands.module.ts new file mode 100644 index 00000000000..beadeae5882 --- /dev/null +++ b/server/src/tenant-manager/commands/tenant-manager-commands.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; + +import { TenantManagerModule } from 'src/tenant-manager/tenant-manager.module'; +import { DataSourceModule } from 'src/metadata/data-source/data-source.module'; + +import { SyncTenantMetadataCommand } from './sync-tenant-metadata.command'; + +@Module({ + imports: [TenantManagerModule, DataSourceModule], + providers: [SyncTenantMetadataCommand], +}) +export class TenantManagerCommandsModule {} diff --git a/server/src/metadata/tenant-initialisation/standard-objects-prefill-data/standard-objects-prefill-data.ts b/server/src/tenant-manager/standard-objects-prefill-data/standard-objects-prefill-data.ts similarity index 100% rename from server/src/metadata/tenant-initialisation/standard-objects-prefill-data/standard-objects-prefill-data.ts rename to server/src/tenant-manager/standard-objects-prefill-data/standard-objects-prefill-data.ts diff --git a/server/src/metadata/standard-objects/companies/companies.metadata.ts b/server/src/tenant-manager/standard-objects/companies/companies.metadata.ts similarity index 100% rename from server/src/metadata/standard-objects/companies/companies.metadata.ts rename to server/src/tenant-manager/standard-objects/companies/companies.metadata.ts diff --git a/server/src/metadata/standard-objects/standard-object-metadata.ts b/server/src/tenant-manager/standard-objects/standard-object-metadata.ts similarity index 100% rename from server/src/metadata/standard-objects/standard-object-metadata.ts rename to server/src/tenant-manager/standard-objects/standard-object-metadata.ts diff --git a/server/src/metadata/standard-objects/view-fields/view-fields.metadata.ts b/server/src/tenant-manager/standard-objects/view-fields/view-fields.metadata.ts similarity index 100% rename from server/src/metadata/standard-objects/view-fields/view-fields.metadata.ts rename to server/src/tenant-manager/standard-objects/view-fields/view-fields.metadata.ts diff --git a/server/src/metadata/standard-objects/view-filters/view-filters.metadata.ts b/server/src/tenant-manager/standard-objects/view-filters/view-filters.metadata.ts similarity index 97% rename from server/src/metadata/standard-objects/view-filters/view-filters.metadata.ts rename to server/src/tenant-manager/standard-objects/view-filters/view-filters.metadata.ts index 8ee351c41df..4508e848b10 100644 --- a/server/src/metadata/standard-objects/view-filters/view-filters.metadata.ts +++ b/server/src/tenant-manager/standard-objects/view-filters/view-filters.metadata.ts @@ -10,7 +10,7 @@ const viewFiltersMetadata = { { type: 'TEXT', name: 'fieldMetadataId', - label: 'Field Id', + label: 'Field Metadata Id', targetColumnMap: { value: 'fieldMetadataId', }, diff --git a/server/src/metadata/standard-objects/view-sorts/view-sorts.metadata.ts b/server/src/tenant-manager/standard-objects/view-sorts/view-sorts.metadata.ts similarity index 96% rename from server/src/metadata/standard-objects/view-sorts/view-sorts.metadata.ts rename to server/src/tenant-manager/standard-objects/view-sorts/view-sorts.metadata.ts index 596a6b3513e..f00f2b1b2dc 100644 --- a/server/src/metadata/standard-objects/view-sorts/view-sorts.metadata.ts +++ b/server/src/tenant-manager/standard-objects/view-sorts/view-sorts.metadata.ts @@ -10,7 +10,7 @@ const viewSortsMetadata = { { type: 'TEXT', name: 'fieldMetadataId', - label: 'Field Id', + label: 'Field Metadata Id', targetColumnMap: { value: 'fieldMetadataId', }, diff --git a/server/src/metadata/standard-objects/views/views.metadata.ts b/server/src/tenant-manager/standard-objects/views/views.metadata.ts similarity index 96% rename from server/src/metadata/standard-objects/views/views.metadata.ts rename to server/src/tenant-manager/standard-objects/views/views.metadata.ts index c03d1481695..9290c142f5a 100644 --- a/server/src/metadata/standard-objects/views/views.metadata.ts +++ b/server/src/tenant-manager/standard-objects/views/views.metadata.ts @@ -21,7 +21,7 @@ const viewsMetadata = { { type: 'TEXT', name: 'objectMetadataId', - label: 'Object Id', + label: 'Object Metadata Id', targetColumnMap: { value: 'objectMetadataId', }, diff --git a/server/src/metadata/tenant-initialisation/tenant-initialisation.module.ts b/server/src/tenant-manager/tenant-manager.module.ts similarity index 53% rename from server/src/metadata/tenant-initialisation/tenant-initialisation.module.ts rename to server/src/tenant-manager/tenant-manager.module.ts index 07d8d0f735a..05054109143 100644 --- a/server/src/metadata/tenant-initialisation/tenant-initialisation.module.ts +++ b/server/src/tenant-manager/tenant-manager.module.ts @@ -1,24 +1,24 @@ import { Module } from '@nestjs/common'; import { DataSourceModule } from 'src/metadata/data-source/data-source.module'; -import { MigrationRunnerModule } from 'src/metadata/migration-runner/migration-runner.module'; -import { TenantMigrationModule } from 'src/metadata/tenant-migration/tenant-migration.module'; -import { DataSourceMetadataModule } from 'src/metadata/data-source-metadata/data-source-metadata.module'; -import { ObjectMetadataModule } from 'src/metadata/object-metadata/object-metadata.module'; import { FieldMetadataModule } from 'src/metadata/field-metadata/field-metadata.module'; +import { ObjectMetadataModule } from 'src/metadata/object-metadata/object-metadata.module'; +import { TenantMigrationModule } from 'src/metadata/tenant-migration/tenant-migration.module'; +import { TenantMigrationRunnerModule } from 'src/tenant-migration-runner/tenant-migration-runner.module'; +import { TenantDataSourceModule } from 'src/tenant-datasource/tenant-datasource.module'; -import { TenantInitialisationService } from './tenant-initialisation.service'; +import { TenantManagerService } from './tenant-manager.service'; @Module({ imports: [ - DataSourceModule, + TenantDataSourceModule, TenantMigrationModule, - MigrationRunnerModule, + TenantMigrationRunnerModule, ObjectMetadataModule, FieldMetadataModule, - DataSourceMetadataModule, + DataSourceModule, ], - exports: [TenantInitialisationService], - providers: [TenantInitialisationService], + exports: [TenantManagerService], + providers: [TenantManagerService], }) -export class TenantInitialisationModule {} +export class TenantManagerModule {} diff --git a/server/src/tenant-manager/tenant-manager.service.ts b/server/src/tenant-manager/tenant-manager.service.ts new file mode 100644 index 00000000000..9c240b6b01d --- /dev/null +++ b/server/src/tenant-manager/tenant-manager.service.ts @@ -0,0 +1,144 @@ +import { Injectable } from '@nestjs/common'; + +import { DataSourceService } from 'src/metadata/data-source/data-source.service'; +import { FieldMetadataService } from 'src/metadata/field-metadata/field-metadata.service'; +import { ObjectMetadataService } from 'src/metadata/object-metadata/object-metadata.service'; +import { TenantMigrationRunnerService } from 'src/tenant-migration-runner/tenant-migration-runner.service'; +import { TenantMigrationService } from 'src/metadata/tenant-migration/tenant-migration.service'; +import { standardObjectsPrefillData } from 'src/tenant-manager/standard-objects-prefill-data/standard-objects-prefill-data'; +import { TenantDataSourceService } from 'src/tenant-datasource/tenant-datasource.service'; +import { DataSourceEntity } from 'src/metadata/data-source/data-source.entity'; + +import { standardObjectsMetadata } from './standard-objects/standard-object-metadata'; + +@Injectable() +export class TenantManagerService { + constructor( + private readonly tenantDataSourceService: TenantDataSourceService, + private readonly tenantMigrationService: TenantMigrationService, + private readonly migrationRunnerService: TenantMigrationRunnerService, + private readonly objectMetadataService: ObjectMetadataService, + private readonly fieldMetadataService: FieldMetadataService, + private readonly dataSourceService: DataSourceService, + ) {} + + /** + * Init a workspace by creating a new data source and running all migrations + * @param workspaceId + * @returns Promise + */ + public async init(workspaceId: string): Promise { + const schemaName = + await this.tenantDataSourceService.createWorkspaceDBSchema(workspaceId); + + const dataSourceMetadata = + await this.dataSourceService.createDataSourceMetadata( + workspaceId, + schemaName, + ); + + await this.tenantMigrationService.insertStandardMigrations(workspaceId); + + await this.migrationRunnerService.executeMigrationFromPendingMigrations( + workspaceId, + ); + + await this.createStandardObjectsAndFieldsMetadata( + dataSourceMetadata.id, + workspaceId, + ); + + await this.prefillWorkspaceWithStandardObjects( + dataSourceMetadata, + workspaceId, + ); + } + + /** + * + * Create all standard objects and fields metadata for a given workspace + * + * @param dataSourceId + * @param workspaceId + */ + public async createStandardObjectsAndFieldsMetadata( + dataSourceId: string, + workspaceId: string, + ) { + await this.objectMetadataService.createMany( + Object.values(standardObjectsMetadata).map((objectMetadata) => ({ + ...objectMetadata, + dataSourceId, + workspaceId, + isCustom: false, + isActive: true, + fields: objectMetadata.fields.map((field) => ({ + ...field, + workspaceId, + isCustom: false, + isActive: true, + })), + })), + ); + } + + /** + * + * Reset all standard objects and fields metadata for a given workspace + * + * @param dataSourceId + * @param workspaceId + */ + public async resetStandardObjectsAndFieldsMetadata( + dataSourceId: string, + workspaceId: string, + ) { + await this.objectMetadataService.deleteMany({ + workspaceId: { eq: workspaceId }, + }); + + await this.createStandardObjectsAndFieldsMetadata( + dataSourceId, + workspaceId, + ); + } + + /** + * + * We are prefilling a few standard objects with data to make it easier for the user to get started. + * + * @param dataSourceMetadata + * @param workspaceId + */ + private async prefillWorkspaceWithStandardObjects( + dataSourceMetadata: DataSourceEntity, + workspaceId: string, + ) { + const workspaceDataSource = + await this.tenantDataSourceService.connectToWorkspaceDataSource( + workspaceId, + ); + + if (!workspaceDataSource) { + throw new Error('Could not connect to workspace data source'); + } + + standardObjectsPrefillData(workspaceDataSource, dataSourceMetadata.schema); + } + + /** + * + * Delete a workspace by deleting all metadata and the schema + * + * @param workspaceId + */ + public async delete(workspaceId: string): Promise { + // Delete data from metadata tables + await this.fieldMetadataService.deleteFieldsMetadata(workspaceId); + await this.objectMetadataService.deleteObjectsMetadata(workspaceId); + await this.tenantMigrationService.delete(workspaceId); + await this.dataSourceService.delete(workspaceId); + // Delete schema + await this.tenantDataSourceService.deleteWorkspaceDBSchema(workspaceId); + } +} diff --git a/server/src/metadata/commands/run-tenant-migrations.command.ts b/server/src/tenant-migration-runner/commands/run-tenant-migrations.command.ts similarity index 80% rename from server/src/metadata/commands/run-tenant-migrations.command.ts rename to server/src/tenant-migration-runner/commands/run-tenant-migrations.command.ts index 557a2357465..89ec659caee 100644 --- a/server/src/metadata/commands/run-tenant-migrations.command.ts +++ b/server/src/tenant-migration-runner/commands/run-tenant-migrations.command.ts @@ -1,7 +1,7 @@ import { Command, CommandRunner, Option } from 'nest-commander'; import { TenantMigrationService } from 'src/metadata/tenant-migration/tenant-migration.service'; -import { MigrationRunnerService } from 'src/metadata/migration-runner/migration-runner.service'; +import { TenantMigrationRunnerService } from 'src/tenant-migration-runner/tenant-migration-runner.service'; // TODO: implement dry-run interface RunTenantMigrationsOptions { @@ -15,7 +15,7 @@ interface RunTenantMigrationsOptions { export class RunTenantMigrationsCommand extends CommandRunner { constructor( private readonly tenantMigrationService: TenantMigrationService, - private readonly migrationRunnerService: MigrationRunnerService, + private readonly tenantMigrationRunnerService: TenantMigrationRunnerService, ) { super(); } @@ -28,7 +28,7 @@ export class RunTenantMigrationsCommand extends CommandRunner { await this.tenantMigrationService.insertStandardMigrations( options.workspaceId, ); - await this.migrationRunnerService.executeMigrationFromPendingMigrations( + await this.tenantMigrationRunnerService.executeMigrationFromPendingMigrations( options.workspaceId, ); } diff --git a/server/src/tenant-migration-runner/commands/tenant-migration-runner-commands.module.ts b/server/src/tenant-migration-runner/commands/tenant-migration-runner-commands.module.ts new file mode 100644 index 00000000000..dff01df6d37 --- /dev/null +++ b/server/src/tenant-migration-runner/commands/tenant-migration-runner-commands.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; + +import { TenantMigrationModule } from 'src/metadata/tenant-migration/tenant-migration.module'; +import { TenantMigrationRunnerModule } from 'src/tenant-migration-runner/tenant-migration-runner.module'; + +import { RunTenantMigrationsCommand } from './run-tenant-migrations.command'; + +@Module({ + imports: [TenantMigrationModule, TenantMigrationRunnerModule], + providers: [RunTenantMigrationsCommand], +}) +export class TenantMigrationRunnerCommandsModule {} diff --git a/server/src/tenant-migration-runner/tenant-migration-runner.module.ts b/server/src/tenant-migration-runner/tenant-migration-runner.module.ts new file mode 100644 index 00000000000..a96340cf387 --- /dev/null +++ b/server/src/tenant-migration-runner/tenant-migration-runner.module.ts @@ -0,0 +1,13 @@ +import { Module } from '@nestjs/common'; + +import { TenantMigrationModule } from 'src/metadata/tenant-migration/tenant-migration.module'; +import { TenantDataSourceModule } from 'src/tenant-datasource/tenant-datasource.module'; + +import { TenantMigrationRunnerService } from './tenant-migration-runner.service'; + +@Module({ + imports: [TenantDataSourceModule, TenantMigrationModule], + exports: [TenantMigrationRunnerService], + providers: [TenantMigrationRunnerService], +}) +export class TenantMigrationRunnerModule {} diff --git a/server/src/metadata/migration-runner/migration-runner.service.ts b/server/src/tenant-migration-runner/tenant-migration-runner.service.ts similarity index 92% rename from server/src/metadata/migration-runner/migration-runner.service.ts rename to server/src/tenant-migration-runner/tenant-migration-runner.service.ts index ca219310385..d9fe0c77669 100644 --- a/server/src/metadata/migration-runner/migration-runner.service.ts +++ b/server/src/tenant-migration-runner/tenant-migration-runner.service.ts @@ -2,22 +2,22 @@ import { Injectable } from '@nestjs/common'; import { QueryRunner, Table, TableColumn, TableForeignKey } from 'typeorm'; -import { DataSourceService } from 'src/metadata/data-source/data-source.service'; +import { TenantMigrationService } from 'src/metadata/tenant-migration/tenant-migration.service'; +import { TenantDataSourceService } from 'src/tenant-datasource/tenant-datasource.service'; import { TenantMigrationTableAction, TenantMigrationColumnAction, + TenantMigrationColumnActionType, TenantMigrationColumnCreate, TenantMigrationColumnRelation, - TenantMigrationColumnActionType, } from 'src/metadata/tenant-migration/tenant-migration.entity'; -import { TenantMigrationService } from 'src/metadata/tenant-migration/tenant-migration.service'; -import { customTableDefaultColumns } from './custom-table-default-column.util'; +import { customTableDefaultColumns } from './utils/custom-table-default-column.util'; @Injectable() -export class MigrationRunnerService { +export class TenantMigrationRunnerService { constructor( - private readonly dataSourceService: DataSourceService, + private readonly tenantDataSourceService: TenantDataSourceService, private readonly tenantMigrationService: TenantMigrationService, ) {} @@ -31,7 +31,9 @@ export class MigrationRunnerService { workspaceId: string, ): Promise { const workspaceDataSource = - await this.dataSourceService.connectToWorkspaceDataSource(workspaceId); + await this.tenantDataSourceService.connectToWorkspaceDataSource( + workspaceId, + ); if (!workspaceDataSource) { throw new Error('Workspace data source not found'); @@ -50,7 +52,7 @@ export class MigrationRunnerService { }, []); const queryRunner = workspaceDataSource?.createQueryRunner(); - const schemaName = this.dataSourceService.getSchemaName(workspaceId); + const schemaName = this.tenantDataSourceService.getSchemaName(workspaceId); // Loop over each migration and create or update the table // TODO: Should be done in a transaction diff --git a/server/src/metadata/migration-runner/custom-table-default-column.util.ts b/server/src/tenant-migration-runner/utils/custom-table-default-column.util.ts similarity index 100% rename from server/src/metadata/migration-runner/custom-table-default-column.util.ts rename to server/src/tenant-migration-runner/utils/custom-table-default-column.util.ts diff --git a/server/src/tenant/query-builder/factories/composite-field-alias.factory.ts b/server/src/tenant/query-builder/factories/composite-field-alias.factory.ts index 6baaedfaf43..fd102241060 100644 --- a/server/src/tenant/query-builder/factories/composite-field-alias.factory.ts +++ b/server/src/tenant/query-builder/factories/composite-field-alias.factory.ts @@ -66,7 +66,7 @@ export class CompositeFieldAliasFactory { const targetTableName = relationMetadata.toObjectMetadata.targetTableName; const relationDirection = deduceRelationDirection( - fieldMetadata.objectId, + fieldMetadata.objectMetadataId, relationMetadata, ); diff --git a/server/src/tenant/query-runner/query-runner.module.ts b/server/src/tenant/query-runner/query-runner.module.ts index 3ad6c59b919..e93d862cd6a 100644 --- a/server/src/tenant/query-runner/query-runner.module.ts +++ b/server/src/tenant/query-runner/query-runner.module.ts @@ -1,12 +1,12 @@ import { Module } from '@nestjs/common'; import { QueryBuilderModule } from 'src/tenant/query-builder/query-builder.module'; -import { DataSourceModule } from 'src/metadata/data-source/data-source.module'; +import { TenantDataSourceModule } from 'src/tenant-datasource/tenant-datasource.module'; import { QueryRunnerService } from './query-runner.service'; @Module({ - imports: [QueryBuilderModule, DataSourceModule], + imports: [QueryBuilderModule, TenantDataSourceModule], providers: [QueryRunnerService], exports: [QueryRunnerService], }) diff --git a/server/src/tenant/query-runner/query-runner.service.ts b/server/src/tenant/query-runner/query-runner.service.ts index ba7dd1ba087..763fd4a753f 100644 --- a/server/src/tenant/query-runner/query-runner.service.ts +++ b/server/src/tenant/query-runner/query-runner.service.ts @@ -16,8 +16,8 @@ import { } from 'src/tenant/query-builder/interfaces/resolvers-builder.interface'; import { QueryBuilderFactory } from 'src/tenant/query-builder/query-builder.factory'; -import { DataSourceService } from 'src/metadata/data-source/data-source.service'; import { parseResult } from 'src/tenant/query-runner/utils/parse-result.util'; +import { TenantDataSourceService } from 'src/tenant-datasource/tenant-datasource.service'; import { QueryRunnerOptions } from './interfaces/query-runner-optionts.interface'; import { @@ -31,7 +31,7 @@ export class QueryRunnerService { constructor( private readonly queryBuilderFactory: QueryBuilderFactory, - private readonly dataSourceService: DataSourceService, + private readonly tenantDataSourceService: TenantDataSourceService, ) {} async findMany< @@ -130,10 +130,14 @@ export class QueryRunnerService { workspaceId: string, ): Promise { const workspaceDataSource = - await this.dataSourceService.connectToWorkspaceDataSource(workspaceId); + await this.tenantDataSourceService.connectToWorkspaceDataSource( + workspaceId, + ); await workspaceDataSource?.query(` - SET search_path TO ${this.dataSourceService.getSchemaName(workspaceId)}; + SET search_path TO ${this.tenantDataSourceService.getSchemaName( + workspaceId, + )}; `); return workspaceDataSource?.query(` diff --git a/server/src/tenant/schema-builder/factories/extend-object-type-definition.factory.ts b/server/src/tenant/schema-builder/factories/extend-object-type-definition.factory.ts index 2363e3de2f4..f0c28685920 100644 --- a/server/src/tenant/schema-builder/factories/extend-object-type-definition.factory.ts +++ b/server/src/tenant/schema-builder/factories/extend-object-type-definition.factory.ts @@ -134,7 +134,7 @@ export class ExtendObjectTypeDefinitionFactory { } const relationDirection = deduceRelationDirection( - fieldMetadata.objectId, + fieldMetadata.objectMetadataId, relationMetadata, ); const relationType = this.relationTypeFactory.create( diff --git a/server/src/metadata/field-metadata/interfaces/field-metadata-target-column-map.interface.ts b/server/src/tenant/schema-builder/interfaces/field-metadata-target-column-map.interface.ts similarity index 100% rename from server/src/metadata/field-metadata/interfaces/field-metadata-target-column-map.interface.ts rename to server/src/tenant/schema-builder/interfaces/field-metadata-target-column-map.interface.ts diff --git a/server/src/tenant/schema-builder/interfaces/field-metadata.interface.ts b/server/src/tenant/schema-builder/interfaces/field-metadata.interface.ts index a6167b75c5b..924b57bcbe9 100644 --- a/server/src/tenant/schema-builder/interfaces/field-metadata.interface.ts +++ b/server/src/tenant/schema-builder/interfaces/field-metadata.interface.ts @@ -1,7 +1,7 @@ -import { FieldMetadataTargetColumnMap } from 'src/metadata/field-metadata/interfaces/field-metadata-target-column-map.interface'; +import { FieldMetadataTargetColumnMap } from 'src/tenant/schema-builder/interfaces/field-metadata-target-column-map.interface'; import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity'; -import { RelationMetadata } from 'src/metadata/relation-metadata/relation-metadata.entity'; +import { RelationMetadataEntity } from 'src/metadata/relation-metadata/relation-metadata.entity'; export interface FieldMetadataInterface< T extends FieldMetadataType | 'default' = 'default', @@ -11,9 +11,9 @@ export interface FieldMetadataInterface< name: string; label: string; targetColumnMap: FieldMetadataTargetColumnMap; - objectId: string; + objectMetadataId: string; description?: string; isNullable?: boolean; - fromRelationMetadata?: RelationMetadata; - toRelationMetadata?: RelationMetadata; + fromRelationMetadata?: RelationMetadataEntity; + toRelationMetadata?: RelationMetadataEntity; } diff --git a/server/src/tenant/schema-builder/object-definitions/money.object-definition.ts b/server/src/tenant/schema-builder/object-definitions/money.object-definition.ts index 28e422ee1a5..01b65f5c6a6 100644 --- a/server/src/tenant/schema-builder/object-definitions/money.object-definition.ts +++ b/server/src/tenant/schema-builder/object-definitions/money.object-definition.ts @@ -1,4 +1,5 @@ import { ObjectMetadataInterface } from 'src/tenant/schema-builder/interfaces/object-metadata.interface'; +import { FieldMetadataInterface } from 'src/tenant/schema-builder/interfaces/field-metadata.interface'; import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity'; @@ -13,21 +14,21 @@ export const moneyObjectDefinition = { { id: 'amount', type: FieldMetadataType.NUMBER, - objectId: FieldMetadataType.MONEY.toString(), + objectMetadataId: FieldMetadataType.MONEY.toString(), name: 'amount', label: 'Amount', targetColumnMap: { value: 'amount' }, isNullable: true, - }, + } satisfies FieldMetadataInterface, { id: 'currency', type: FieldMetadataType.TEXT, - objectId: FieldMetadataType.MONEY.toString(), + objectMetadataId: FieldMetadataType.MONEY.toString(), name: 'currency', label: 'Currency', targetColumnMap: { value: 'currency' }, - }, + } satisfies FieldMetadataInterface, ], fromRelations: [], toRelations: [], -} as ObjectMetadataInterface; +} satisfies ObjectMetadataInterface; diff --git a/server/src/tenant/schema-builder/object-definitions/url.object-definition.ts b/server/src/tenant/schema-builder/object-definitions/url.object-definition.ts index 446109c035d..f2e5168b037 100644 --- a/server/src/tenant/schema-builder/object-definitions/url.object-definition.ts +++ b/server/src/tenant/schema-builder/object-definitions/url.object-definition.ts @@ -1,4 +1,5 @@ import { ObjectMetadataInterface } from 'src/tenant/schema-builder/interfaces/object-metadata.interface'; +import { FieldMetadataInterface } from 'src/tenant/schema-builder/interfaces/field-metadata.interface'; import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity'; @@ -13,20 +14,20 @@ export const urlObjectDefinition = { { id: 'text', type: FieldMetadataType.TEXT, - objectId: FieldMetadataType.URL.toString(), + objectMetadataId: FieldMetadataType.URL.toString(), name: 'text', label: 'Text', targetColumnMap: { value: 'text' }, - }, + } satisfies FieldMetadataInterface, { id: 'link', type: FieldMetadataType.TEXT, - objectId: FieldMetadataType.URL.toString(), + objectMetadataId: FieldMetadataType.URL.toString(), name: 'link', label: 'Link', targetColumnMap: { value: 'link' }, - }, + } satisfies FieldMetadataInterface, ], fromRelations: [], toRelations: [], -} as ObjectMetadataInterface; +} satisfies ObjectMetadataInterface; diff --git a/server/src/tenant/schema-builder/type-definitions.generator.ts b/server/src/tenant/schema-builder/type-definitions.generator.ts index d69a86a56f7..642ada9e010 100644 --- a/server/src/tenant/schema-builder/type-definitions.generator.ts +++ b/server/src/tenant/schema-builder/type-definitions.generator.ts @@ -1,7 +1,7 @@ import { Injectable, Logger } from '@nestjs/common'; -import { FieldMetadata } from 'src/metadata/field-metadata/field-metadata.entity'; -import { customTableDefaultColumns } from 'src/metadata/migration-runner/custom-table-default-column.util'; +import { FieldMetadataEntity } from 'src/metadata/field-metadata/field-metadata.entity'; +import { customTableDefaultColumns } from 'src/tenant-migration-runner/utils/custom-table-default-column.util'; import { TypeDefinitionsStorage } from './storages/type-definitions.storage'; import { @@ -31,7 +31,7 @@ const defaultFields = customTableDefaultColumns.map((column) => { type: getFieldMetadataType(column.type), name: column.name, isNullable: true, - } as FieldMetadata; + } as FieldMetadataEntity; }); @Injectable() diff --git a/server/src/tenant/tenant.module.ts b/server/src/tenant/tenant.module.ts index f172f407efe..c14772261f6 100644 --- a/server/src/tenant/tenant.module.ts +++ b/server/src/tenant/tenant.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; import { MetadataModule } from 'src/metadata/metadata.module'; -import { DataSourceMetadataModule } from 'src/metadata/data-source-metadata/data-source-metadata.module'; +import { DataSourceModule } from 'src/metadata/data-source/data-source.module'; import { ObjectMetadataModule } from 'src/metadata/object-metadata/object-metadata.module'; import { TenantService } from './tenant.service'; @@ -12,7 +12,7 @@ import { ResolverBuilderModule } from './resolver-builder/resolver-builder.modul @Module({ imports: [ MetadataModule, - DataSourceMetadataModule, + DataSourceModule, ObjectMetadataModule, SchemaBuilderModule, ResolverBuilderModule, diff --git a/server/src/tenant/tenant.service.spec.ts b/server/src/tenant/tenant.service.spec.ts index a8c64dd979b..d6db3d66c00 100644 --- a/server/src/tenant/tenant.service.spec.ts +++ b/server/src/tenant/tenant.service.spec.ts @@ -1,7 +1,7 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { DataSourceMetadataService } from 'src/metadata/data-source-metadata/data-source-metadata.service'; -import { ObjectMetadataService } from 'src/metadata/object-metadata/services/object-metadata.service'; +import { DataSourceService } from 'src/metadata/data-source/data-source.service'; +import { ObjectMetadataService } from 'src/metadata/object-metadata/object-metadata.service'; import { TenantService } from './tenant.service'; @@ -16,7 +16,7 @@ describe('TenantService', () => { providers: [ TenantService, { - provide: DataSourceMetadataService, + provide: DataSourceService, useValue: {}, }, { diff --git a/server/src/tenant/tenant.service.ts b/server/src/tenant/tenant.service.ts index 679e09e11ab..6c35a1f2641 100644 --- a/server/src/tenant/tenant.service.ts +++ b/server/src/tenant/tenant.service.ts @@ -5,8 +5,8 @@ import { GraphQLSchema, printSchema } from 'graphql'; import { makeExecutableSchema } from '@graphql-tools/schema'; import { gql } from 'graphql-tag'; -import { DataSourceMetadataService } from 'src/metadata/data-source-metadata/data-source-metadata.service'; -import { ObjectMetadataService } from 'src/metadata/object-metadata/services/object-metadata.service'; +import { DataSourceService } from 'src/metadata/data-source/data-source.service'; +import { ObjectMetadataService } from 'src/metadata/object-metadata/object-metadata.service'; import { GraphQLSchemaFactory } from './schema-builder/graphql-schema.factory'; import { resolverBuilderMethodNames } from './resolver-builder/factories/factories'; @@ -15,7 +15,7 @@ import { ResolverFactory } from './resolver-builder/resolver.factory'; @Injectable() export class TenantService { constructor( - private readonly dataSourceMetadataService: DataSourceMetadataService, + private readonly dataSourceService: DataSourceService, private readonly objectMetadataService: ObjectMetadataService, private readonly graphQLSchemaFactory: GraphQLSchemaFactory, private readonly resolverFactory: ResolverFactory, @@ -27,7 +27,7 @@ export class TenantService { } const dataSourcesMetadata = - await this.dataSourceMetadataService.getDataSourcesMetadataFromWorkspaceId( + await this.dataSourceService.getDataSourcesMetadataFromWorkspaceId( workspaceId, );