Revert "Convert metadata tables to camelCase" (#2419)

Revert "Convert metadata tables to camelCase (#2400)"

This reverts commit 1cf08c797f.
This commit is contained in:
Charles Bochet
2023-11-10 11:48:44 +01:00
committed by GitHub
parent 1cf08c797f
commit 7b9175a4a4
238 changed files with 2253 additions and 1852 deletions

View File

@ -1,66 +0,0 @@
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 { TenantMigrationService } from 'src/metadata/tenant-migration/tenant-migration.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({
name: 'tenant:seed',
description:
'Seed tenant with initial data. This command is intended for development only.',
})
export class DataSeedTenantCommand extends CommandRunner {
workspaceId = 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419';
constructor(
@InjectDataSource('metadata')
private readonly metadataDataSource: DataSource,
private readonly dataSourceMetadataService: DataSourceMetadataService,
private readonly typeORMService: TypeORMService,
private readonly tenantMigrationService: TenantMigrationService,
private readonly migrationRunnerService: TenantMigrationRunnerService,
) {
super();
}
async run(): Promise<void> {
const dataSourceMetadata =
await this.dataSourceMetadataService.getLastDataSourceMetadataFromWorkspaceIdOrFail(
this.workspaceId,
);
const workspaceDataSource = await this.typeORMService.connectToDataSource(
dataSourceMetadata,
);
if (!workspaceDataSource) {
throw new Error('Could not connect to workspace data source');
}
await seedObjectMetadata(this.metadataDataSource, 'metadata');
await seedFieldMetadata(this.metadataDataSource, 'metadata');
await this.tenantMigrationService.insertStandardMigrations(
this.workspaceId,
);
await this.migrationRunnerService.executeMigrationFromPendingMigrations(
this.workspaceId,
);
await seedCompanies(workspaceDataSource, dataSourceMetadata.schema);
await seedViewFields(workspaceDataSource, dataSourceMetadata.schema);
await seedViews(workspaceDataSource, dataSourceMetadata.schema);
await this.typeORMService.disconnectFromDataSource(dataSourceMetadata.id);
}
}

View File

@ -2,36 +2,21 @@ 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 { DataSourceMetadataModule } from 'src/metadata/data-source-metadata/data-source-metadata.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,
TenantManagerModule,
TenantInitialisationModule,
PrismaModule,
DataSourceMetadataModule,
TypeORMModule,
TenantMigrationModule,
TenantMigrationRunnerModule,
WorkspaceModule,
],
providers: [
DataSeedTenantCommand,
DataCleanInactiveCommand,
ConfirmationQuestion,
],
providers: [DataCleanInactiveCommand, ConfirmationQuestion, WorkspaceService],
})
export class DatabaseCommandModule {}

View File

@ -5,8 +5,8 @@ export const seedMetadata = async (prisma: PrismaClient) => {
'CREATE SCHEMA IF NOT EXISTS workspace_twenty_7icsva0r6s00mpcp6cwg4w4rd',
);
await prisma.$queryRawUnsafe(
`INSERT INTO metadata."dataSource"(
id, schema, type, "workspaceId"
`INSERT INTO metadata.data_source_metadata(
id, schema, type, workspace_id
)
VALUES (
'b37b2163-7f63-47a9-b1b3-6c7290ca9fb1', 'workspace_twenty_7icsva0r6s00mpcp6cwg4w4rd', 'postgres', 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419'

View File

@ -1,6 +1,6 @@
import { DataSource } from 'typeorm';
const tableName = 'fieldMetadata';
const tableName = 'field_metadata';
export const seedFieldMetadata = async (
workspaceDataSource: DataSource,
@ -10,7 +10,7 @@ export const seedFieldMetadata = async (
.createQueryBuilder()
.insert()
.into(`${schemaName}.${tableName}`, [
'objectMetadataId',
'objectId',
'isCustom',
'workspaceId',
'isActive',
@ -26,7 +26,7 @@ export const seedFieldMetadata = async (
.values([
// Companies
{
objectMetadataId: '1a8487a0-480c-434e-b4c7-e22408b97047',
objectId: '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,
},
{
objectMetadataId: '1a8487a0-480c-434e-b4c7-e22408b97047',
objectId: '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,
},
{
objectMetadataId: '1a8487a0-480c-434e-b4c7-e22408b97047',
objectId: '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,
},
{
objectMetadataId: '1a8487a0-480c-434e-b4c7-e22408b97047',
objectId: '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
{
objectMetadataId: '9ab6b3dc-767f-473f-8fd0-6cdbefbf8dbe',
objectId: '9ab6b3dc-767f-473f-8fd0-6cdbefbf8dbe',
isCustom: false,
workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419',
isActive: true,
@ -102,22 +102,22 @@ export const seedFieldMetadata = async (
isNullable: false,
},
{
objectMetadataId: '9ab6b3dc-767f-473f-8fd0-6cdbefbf8dbe',
objectId: '9ab6b3dc-767f-473f-8fd0-6cdbefbf8dbe',
isCustom: false,
workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419',
isActive: true,
type: 'TEXT',
name: 'objectMetadataId',
name: 'objectId',
label: 'Object Id',
targetColumnMap: {
value: 'objectMetadataId',
value: 'objectId',
},
description: 'View target object',
icon: null,
isNullable: false,
},
{
objectMetadataId: '9ab6b3dc-767f-473f-8fd0-6cdbefbf8dbe',
objectId: '9ab6b3dc-767f-473f-8fd0-6cdbefbf8dbe',
isCustom: false,
workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419',
isActive: true,
@ -133,22 +133,22 @@ export const seedFieldMetadata = async (
},
// View Fields
{
objectMetadataId: '61d9000b-485c-4c48-a22e-0d9a164f9647',
objectId: '61d9000b-485c-4c48-a22e-0d9a164f9647',
isCustom: false,
workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419',
isActive: true,
type: 'TEXT',
name: 'fieldMetadataId',
name: 'fieldId',
label: 'Field Id',
targetColumnMap: {
value: 'fieldMetadataId',
value: 'fieldId',
},
description: 'View Field target field',
icon: null,
isNullable: false,
},
{
objectMetadataId: '61d9000b-485c-4c48-a22e-0d9a164f9647',
objectId: '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,
},
{
objectMetadataId: '61d9000b-485c-4c48-a22e-0d9a164f9647',
objectId: '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,
},
{
objectMetadataId: '61d9000b-485c-4c48-a22e-0d9a164f9647',
objectId: '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,
},
{
objectMetadataId: '61d9000b-485c-4c48-a22e-0d9a164f9647',
objectId: '61d9000b-485c-4c48-a22e-0d9a164f9647',
isCustom: false,
workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419',
isActive: true,
@ -209,22 +209,22 @@ export const seedFieldMetadata = async (
},
// View Filters
{
objectMetadataId: '5d9b1ab9-4461-4e2d-bf9e-9b47e68846d3',
objectId: '5d9b1ab9-4461-4e2d-bf9e-9b47e68846d3',
isCustom: false,
workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419',
isActive: true,
type: 'TEXT',
name: 'fieldMetadataId',
name: 'fieldId',
label: 'Field Id',
targetColumnMap: {
value: 'fieldMetadataId',
value: 'fieldId',
},
description: 'View Filter target field',
icon: null,
isNullable: false,
},
{
objectMetadataId: '5d9b1ab9-4461-4e2d-bf9e-9b47e68846d3',
objectId: '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,
},
{
objectMetadataId: '5d9b1ab9-4461-4e2d-bf9e-9b47e68846d3',
objectId: '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,
},
{
objectMetadataId: '5d9b1ab9-4461-4e2d-bf9e-9b47e68846d3',
objectId: '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,
},
{
objectMetadataId: '5d9b1ab9-4461-4e2d-bf9e-9b47e68846d3',
objectId: '5d9b1ab9-4461-4e2d-bf9e-9b47e68846d3',
isCustom: false,
workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419',
isActive: true,
@ -285,22 +285,22 @@ export const seedFieldMetadata = async (
},
// View Sorts
{
objectMetadataId: '6f8dcd4b-cf28-41dd-b98b-d6e1f5b3a251',
objectId: '6f8dcd4b-cf28-41dd-b98b-d6e1f5b3a251',
isCustom: false,
workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419',
isActive: true,
type: 'TEXT',
name: 'fieldMetadataId',
name: 'fieldId',
label: 'Field Id',
targetColumnMap: {
value: 'fieldMetadataId',
value: 'fieldId',
},
description: 'View Sort target field',
icon: null,
isNullable: false,
},
{
objectMetadataId: '6f8dcd4b-cf28-41dd-b98b-d6e1f5b3a251',
objectId: '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,
},
{
objectMetadataId: '6f8dcd4b-cf28-41dd-b98b-d6e1f5b3a251',
objectId: '6f8dcd4b-cf28-41dd-b98b-d6e1f5b3a251',
isCustom: false,
workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419',
isActive: true,

View File

@ -1,6 +1,6 @@
import { DataSource } from 'typeorm';
const tableName = 'objectMetadata';
const tableName = 'object_metadata';
export const seedObjectMetadata = async (
workspaceDataSource: DataSource,

View File

@ -11,7 +11,7 @@ export const seedViewFields = async (
.insert()
.into(`${schemaName}.${tableName}`, [
'id',
'fieldMetadataId',
'fieldId',
'viewId',
'position',
'isVisible',
@ -21,7 +21,7 @@ export const seedViewFields = async (
.values([
{
id: '46a72a5b-276e-4241-a05f-c054410aebcb',
fieldMetadataId: 'name',
fieldId: 'name',
viewId: '10bec73c-0aea-4cc4-a3b2-8c2186f29b43',
position: 0,
isVisible: true,
@ -29,7 +29,7 @@ export const seedViewFields = async (
},
{
id: 'f15b26ff-8f79-49dd-8f53-4286dd1af846',
fieldMetadataId: 'name',
fieldId: 'name',
viewId: '37a8a866-eb17-4e76-9382-03143a2f6a80',
position: 0,
isVisible: true,
@ -37,7 +37,7 @@ export const seedViewFields = async (
},
{
id: '8d1dbb50-c97f-42c4-8575-3d2c9bdeb6e5',
fieldMetadataId: 'domainName',
fieldId: 'domainName',
viewId: '37a8a866-eb17-4e76-9382-03143a2f6a80',
position: 1,
isVisible: true,
@ -45,7 +45,7 @@ export const seedViewFields = async (
},
{
id: '33833b3b-4e02-4f10-91fc-c594422952af',
fieldMetadataId: 'accountOwner',
fieldId: 'accountOwner',
viewId: '37a8a866-eb17-4e76-9382-03143a2f6a80',
position: 2,
isVisible: true,
@ -53,7 +53,7 @@ export const seedViewFields = async (
},
{
id: 'c750a968-832e-4812-a1a2-74f515af55c1',
fieldMetadataId: 'createdAt',
fieldId: 'createdAt',
viewId: '37a8a866-eb17-4e76-9382-03143a2f6a80',
position: 3,
isVisible: true,
@ -61,7 +61,7 @@ export const seedViewFields = async (
},
{
id: '2fde3187-a0bc-47ca-80bd-457bd826fb4a',
fieldMetadataId: 'employees',
fieldId: 'employees',
viewId: '37a8a866-eb17-4e76-9382-03143a2f6a80',
position: 4,
isVisible: true,
@ -69,7 +69,7 @@ export const seedViewFields = async (
},
{
id: '2fead26f-3f4f-4a4d-a4c6-3abe7b2f74c9',
fieldMetadataId: 'linkedin',
fieldId: 'linkedin',
viewId: '37a8a866-eb17-4e76-9382-03143a2f6a80',
position: 5,
isVisible: true,
@ -77,7 +77,7 @@ export const seedViewFields = async (
},
{
id: '0cffa82a-c851-4e17-b46c-2c4642d78329',
fieldMetadataId: 'address',
fieldId: 'address',
viewId: '37a8a866-eb17-4e76-9382-03143a2f6a80',
position: 6,
isVisible: true,
@ -85,7 +85,7 @@ export const seedViewFields = async (
},
{
id: '93a68c4a-8107-409a-9adb-06305ffbd692',
fieldMetadataId: 'displayName',
fieldId: 'displayName',
viewId: '6095799e-b48f-4e00-b071-10818083593a',
position: 0,
isVisible: true,
@ -93,7 +93,7 @@ export const seedViewFields = async (
},
{
id: 'd955ee31-6316-4cb2-af71-9609dede4d7e',
fieldMetadataId: 'email',
fieldId: 'email',
viewId: '6095799e-b48f-4e00-b071-10818083593a',
position: 1,
isVisible: true,
@ -101,7 +101,7 @@ export const seedViewFields = async (
},
{
id: 'bceb4d84-8ad1-4a0e-9333-efb870b42eb8',
fieldMetadataId: 'company',
fieldId: 'company',
viewId: '6095799e-b48f-4e00-b071-10818083593a',
position: 2,
isVisible: true,
@ -109,7 +109,7 @@ export const seedViewFields = async (
},
{
id: 'bef874d4-f349-4cdb-ae28-6e9fc497449b',
fieldMetadataId: 'phone',
fieldId: 'phone',
viewId: '6095799e-b48f-4e00-b071-10818083593a',
position: 3,
isVisible: true,
@ -117,7 +117,7 @@ export const seedViewFields = async (
},
{
id: 'e06f920d-1af9-404d-8b9a-4f97c4009a4a',
fieldMetadataId: 'createdAt',
fieldId: 'createdAt',
viewId: '6095799e-b48f-4e00-b071-10818083593a',
position: 4,
isVisible: true,
@ -125,7 +125,7 @@ export const seedViewFields = async (
},
{
id: '92d94ee8-31fc-4025-a427-29291abb2b19',
fieldMetadataId: 'city',
fieldId: 'city',
viewId: '6095799e-b48f-4e00-b071-10818083593a',
position: 5,
isVisible: true,
@ -133,7 +133,7 @@ export const seedViewFields = async (
},
{
id: 'b38e4022-1559-40da-bd5e-29d89b6c8330',
fieldMetadataId: 'jobTitle',
fieldId: 'jobTitle',
viewId: '6095799e-b48f-4e00-b071-10818083593a',
position: 6,
isVisible: true,
@ -141,7 +141,7 @@ export const seedViewFields = async (
},
{
id: '30147fab-9666-4db5-a11b-20af4544c712',
fieldMetadataId: 'linkedin',
fieldId: 'linkedin',
viewId: '6095799e-b48f-4e00-b071-10818083593a',
position: 7,
isVisible: true,
@ -149,7 +149,7 @@ export const seedViewFields = async (
},
{
id: 'f0870949-21ac-46a2-b3ec-d1b0107c434c',
fieldMetadataId: 'x',
fieldId: 'x',
viewId: '6095799e-b48f-4e00-b071-10818083593a',
position: 8,
isVisible: true,

View File

@ -9,36 +9,31 @@ export const seedViews = async (
await workspaceDataSource
.createQueryBuilder()
.insert()
.into(`${schemaName}.${tableName}`, [
'id',
'name',
'objectMetadataId',
'type',
])
.into(`${schemaName}.${tableName}`, ['id', 'name', 'objectId', 'type'])
.orIgnore()
.values([
{
id: '37a8a866-eb17-4e76-9382-03143a2f6a80',
name: 'All companies',
objectMetadataId: 'company',
objectId: 'company',
type: 'table',
},
{
id: '6095799e-b48f-4e00-b071-10818083593a',
name: 'All people',
objectMetadataId: 'person',
objectId: 'person',
type: 'table',
},
{
id: 'e26f66b7-f890-4a5c-b4d2-ec09987b5308',
name: 'All opportunities',
objectMetadataId: 'company',
objectId: 'company',
type: 'kanban',
},
{
id: '10bec73c-0aea-4cc4-a3b2-8c2186f29b43',
name: 'All Companies (V2)',
objectMetadataId: '1a8487a0-480c-434e-b4c7-e22408b97047',
objectId: '1a8487a0-480c-434e-b4c7-e22408b97047',
type: 'table',
},
])

View File

@ -1,40 +0,0 @@
import {
DataSourceOptions,
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
UpdateDateColumn,
} from 'typeorm';
type DataSourceType = DataSourceOptions['type'];
@Entity('dataSource')
export class DataSourceEntity {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column({ nullable: true })
url: string;
@Column({ nullable: true })
schema: string;
@Column({ type: 'enum', enum: ['postgres'], default: 'postgres' })
type: DataSourceType;
@Column({ nullable: true })
label: string;
@Column({ default: false })
isRemote: boolean;
@Column({ nullable: false })
workspaceId: string;
@CreateDateColumn()
createdAt: Date;
@UpdateDateColumn()
updatedAt: Date;
}

View File

@ -1,102 +0,0 @@
import {
Entity,
Unique,
PrimaryGeneratedColumn,
Column,
ManyToOne,
JoinColumn,
OneToOne,
CreateDateColumn,
UpdateDateColumn,
} from 'typeorm';
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 { ObjectMetadataEntity } from './object-metadata.entity';
import { RelationMetadataEntity } from './relation-metadata.entity';
export enum FieldMetadataType {
UUID = 'uuid',
TEXT = 'TEXT',
PHONE = 'PHONE',
EMAIL = 'EMAIL',
DATE = 'DATE',
BOOLEAN = 'BOOLEAN',
NUMBER = 'NUMBER',
ENUM = 'ENUM',
URL = 'URL',
MONEY = 'MONEY',
RELATION = 'RELATION',
}
@Entity('fieldMetadata')
@Unique('IndexOnNameObjectMetadataIdAndWorkspaceIdUnique', [
'name',
'objectMetadataId',
'workspaceId',
])
export class FieldMetadataEntity implements FieldMetadataInterface {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column({ nullable: false, type: 'uuid' })
objectMetadataId: string;
@ManyToOne(() => ObjectMetadataEntity, (object) => object.fields, {
onDelete: 'CASCADE',
})
@JoinColumn({ name: 'objectMetadataId' })
object: ObjectMetadataEntity;
@Column({ nullable: false })
type: FieldMetadataType;
@Column({ nullable: false })
name: string;
@Column({ nullable: false })
label: string;
@Column({ nullable: false, type: 'jsonb' })
targetColumnMap: FieldMetadataTargetColumnMap;
@Column({ nullable: true, type: 'text' })
description: string;
@Column({ nullable: true })
icon: string;
@Column('text', { nullable: true, array: true })
enums: string[];
@Column({ default: false })
isCustom: boolean;
@Column({ default: false })
isActive: boolean;
@Column({ nullable: true, default: true })
isNullable: boolean;
@Column({ nullable: false })
workspaceId: string;
@OneToOne(
() => RelationMetadataEntity,
(relation: RelationMetadataEntity) => relation.fromFieldMetadata,
)
fromRelationMetadata: RelationMetadataEntity;
@OneToOne(
() => RelationMetadataEntity,
(relation: RelationMetadataEntity) => relation.toFieldMetadata,
)
toRelationMetadata: RelationMetadataEntity;
@CreateDateColumn()
createdAt: Date;
@UpdateDateColumn()
updatedAt: Date;
}

View File

@ -1,81 +0,0 @@
import {
Entity,
Unique,
PrimaryGeneratedColumn,
Column,
OneToMany,
CreateDateColumn,
UpdateDateColumn,
} from 'typeorm';
import { ObjectMetadataInterface } from 'src/tenant/schema-builder/interfaces/object-metadata.interface';
import { FieldMetadataEntity } from './field-metadata.entity';
import { RelationMetadataEntity } from './relation-metadata.entity';
@Entity('objectMetadata')
@Unique('IndexOnNameSingularAndWorkspaceIdUnique', [
'nameSingular',
'workspaceId',
])
@Unique('IndexOnNamePluralAndWorkspaceIdUnique', ['namePlural', 'workspaceId'])
export class ObjectMetadataEntity implements ObjectMetadataInterface {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column({ nullable: false, type: 'uuid' })
dataSourceId: string;
@Column({ nullable: false })
nameSingular: string;
@Column({ nullable: false })
namePlural: string;
@Column({ nullable: false })
labelSingular: string;
@Column({ nullable: false })
labelPlural: string;
@Column({ nullable: true, type: 'text' })
description: string;
@Column({ nullable: true })
icon: string;
@Column({ nullable: false })
targetTableName: string;
@Column({ default: false })
isCustom: boolean;
@Column({ default: false })
isActive: boolean;
@Column({ nullable: false })
workspaceId: string;
@OneToMany(() => FieldMetadataEntity, (field) => field.object, {
cascade: true,
})
fields: FieldMetadataEntity[];
@OneToMany(
() => RelationMetadataEntity,
(relation: RelationMetadataEntity) => relation.fromObjectMetadata,
)
fromRelations: RelationMetadataEntity[];
@OneToMany(
() => RelationMetadataEntity,
(relation: RelationMetadataEntity) => relation.toObjectMetadata,
)
toRelations: RelationMetadataEntity[];
@CreateDateColumn()
createdAt: Date;
@UpdateDateColumn()
updatedAt: Date;
}

View File

@ -1,75 +0,0 @@
import {
Column,
CreateDateColumn,
Entity,
JoinColumn,
ManyToOne,
OneToOne,
PrimaryGeneratedColumn,
UpdateDateColumn,
} from 'typeorm';
import { FieldMetadataEntity } from './field-metadata.entity';
import { ObjectMetadataEntity } from './object-metadata.entity';
export enum RelationType {
ONE_TO_ONE = 'ONE_TO_ONE',
ONE_TO_MANY = 'ONE_TO_MANY',
MANY_TO_MANY = 'MANY_TO_MANY',
}
@Entity('relationMetadata')
export class RelationMetadataEntity {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column({ nullable: false })
relationType: RelationType;
@Column({ nullable: false, type: 'uuid' })
fromObjectMetadataId: string;
@Column({ nullable: false, type: 'uuid' })
toObjectMetadataId: string;
@Column({ nullable: false, type: 'uuid' })
fromFieldMetadataId: string;
@Column({ nullable: false, type: 'uuid' })
toFieldMetadataId: string;
@Column({ nullable: false })
workspaceId: string;
@ManyToOne(
() => ObjectMetadataEntity,
(object: ObjectMetadataEntity) => object.fromRelations,
)
fromObjectMetadata: ObjectMetadataEntity;
@ManyToOne(
() => ObjectMetadataEntity,
(object: ObjectMetadataEntity) => object.toRelations,
)
toObjectMetadata: ObjectMetadataEntity;
@OneToOne(
() => FieldMetadataEntity,
(field: FieldMetadataEntity) => field.fromRelationMetadata,
)
@JoinColumn()
fromFieldMetadata: FieldMetadataEntity;
@OneToOne(
() => FieldMetadataEntity,
(field: FieldMetadataEntity) => field.toRelationMetadata,
)
@JoinColumn()
toFieldMetadata: FieldMetadataEntity;
@CreateDateColumn()
createdAt: Date;
@UpdateDateColumn()
updatedAt: Date;
}

View File

@ -1,57 +0,0 @@
import {
Column,
CreateDateColumn,
Entity,
PrimaryGeneratedColumn,
} from 'typeorm';
export enum TenantMigrationColumnActionType {
CREATE = 'CREATE',
RELATION = 'RELATION',
}
export type TenantMigrationColumnCreate = {
action: TenantMigrationColumnActionType.CREATE;
columnName: string;
columnType: string;
};
export type TenantMigrationColumnRelation = {
action: TenantMigrationColumnActionType.RELATION;
columnName: string;
referencedTableName: string;
referencedTableColumnName: string;
};
export type TenantMigrationColumnAction = {
action: TenantMigrationColumnActionType;
} & (TenantMigrationColumnCreate | TenantMigrationColumnRelation);
export type TenantMigrationTableAction = {
name: string;
action: 'create' | 'alter';
columns?: TenantMigrationColumnAction[];
};
@Entity('tenantMigration')
export class TenantMigrationEntity {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column({ nullable: true, type: 'jsonb' })
migrations: TenantMigrationTableAction[];
@Column({ nullable: true })
name: string;
@Column({ default: false })
isCustom: boolean;
@Column({ nullable: true })
appliedAt?: Date;
@Column()
workspaceId: string;
@CreateDateColumn()
createdAt: Date;
}

View File

@ -1,25 +0,0 @@
import { ConfigService } from '@nestjs/config';
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<string>('PG_DATABASE_URL'),
type: 'postgres',
logging: ['error'],
schema: 'metadata',
entities: [__dirname + '/entities/*.entity{.ts,.js}'],
synchronize: false,
migrationsRun: false,
migrationsTableName: '_typeorm_migrations',
migrations: [__dirname + '/migrations/*{.ts,.js}'],
};
export const connectionSource = new DataSource(
typeORMMetadataModuleOptions as DataSourceOptions,
);

View File

@ -1,65 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class SetupMetadataTables1699543628458 implements MigrationInterface {
name = 'SetupMetadataTables1699543628458';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE "metadata"."objectMetadata" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "dataSourceId" uuid 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"."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"."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 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"."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"."fieldMetadata" ADD CONSTRAINT "FK_de2a09b9e3e690440480d2dee26" FOREIGN KEY ("objectMetadataId") REFERENCES "metadata"."objectMetadata"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
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`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
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(
`ALTER TABLE "metadata"."fieldMetadata" DROP CONSTRAINT "FK_de2a09b9e3e690440480d2dee26"`,
);
await queryRunner.query(`DROP TABLE "metadata"."tenantMigration"`);
await queryRunner.query(`DROP TABLE "metadata"."dataSource"`);
await queryRunner.query(`DROP TYPE "metadata"."dataSource_type_enum"`);
await queryRunner.query(`DROP TABLE "metadata"."relationMetadata"`);
await queryRunner.query(`DROP TABLE "metadata"."fieldMetadata"`);
await queryRunner.query(`DROP TABLE "metadata"."objectMetadata"`);
}
}

View File

@ -1,23 +0,0 @@
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<TypeOrmModuleOptions> => ({
...typeORMMetadataModuleOptions,
name: 'metadata',
});
@Module({
imports: [
TypeOrmModule.forRootAsync({
useFactory: metadataTypeORMFactory,
name: 'metadata',
}),
],
providers: [TypeORMService],
exports: [TypeORMService],
})
export class TypeORMModule {}

View File

@ -1,106 +0,0 @@
import { Injectable, OnModuleDestroy, OnModuleInit } from '@nestjs/common';
import { DataSource } from 'typeorm';
import { EnvironmentService } from 'src/integrations/environment/environment.service';
import { DataSourceEntity } from './metadata/entities/data-source.entity';
@Injectable()
export class TypeORMService implements OnModuleInit, OnModuleDestroy {
private mainDataSource: DataSource;
private dataSources: Map<string, DataSource> = 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<DataSource | undefined>
*/
public async connectToDataSource(
dataSource: DataSourceEntity,
): Promise<DataSource | undefined> {
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<void>
*
*/
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<void>
*/
public async createSchema(schemaName: string): Promise<string> {
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();
}
}
}