Add Tenant initialisation service (#2100)

* Add Tenant initialisation service

* few fixes

* fix constraint

* fix tests

* update metadata json with employees and address

* add V2

* remove metadata.gql
This commit is contained in:
Weiko
2023-10-18 18:01:52 +02:00
committed by GitHub
parent 1cd91e60fa
commit 7fbef6d60d
37 changed files with 513 additions and 177 deletions

View File

@ -0,0 +1,34 @@
import { TenantMigrationTableAction } from 'src/metadata/tenant-migration/tenant-migration.entity';
export const addCompanyTable: TenantMigrationTableAction[] = [
{
name: 'company',
action: 'create',
},
{
name: 'company',
action: 'alter',
columns: [
{
name: 'name',
type: 'varchar',
action: 'create',
},
{
name: 'domainName',
type: 'varchar',
action: 'create',
},
{
name: 'address',
type: 'varchar',
action: 'create',
},
{
name: 'employees',
type: 'integer',
action: 'create',
},
],
},
];

View File

@ -0,0 +1,6 @@
import { addCompanyTable } from './migrations/1697618009-addCompanyTable';
// TODO: read the folder and return all migrations
export const standardMigrations = {
'1697618009-addCompanyTable': addCompanyTable,
};

View File

@ -5,29 +5,37 @@ import {
PrimaryGeneratedColumn,
} from 'typeorm';
export type TenantMigrationColumnChange = {
export type TenantMigrationColumnAction = {
name: string;
type: string;
change: 'create' | 'alter';
action: 'create';
};
export type TenantMigrationTableChange = {
export type TenantMigrationTableAction = {
name: string;
change: 'create' | 'alter';
columns?: TenantMigrationColumnChange[];
action: 'create' | 'alter';
columns?: TenantMigrationColumnAction[];
};
@Entity('tenant_migrations')
export class TenantMigration {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column({ nullable: true, type: 'jsonb' })
migrations: TenantMigrationTableChange[];
migrations: TenantMigrationTableAction[];
@Column({ nullable: true, name: 'applied_at' })
appliedAt: Date;
@Column({ nullable: true })
name: string;
@CreateDateColumn({ name: 'created_at' })
@Column({ default: false })
isCustom: boolean;
@Column({ nullable: true })
appliedAt?: Date;
@Column()
workspaceId: string;
@CreateDateColumn()
createdAt: Date;
}

View File

@ -1,11 +1,11 @@
import { Module } from '@nestjs/common';
import { DataSourceModule } from 'src/metadata/data-source/data-source.module';
import { TypeOrmModule } from '@nestjs/typeorm';
import { TenantMigrationService } from './tenant-migration.service';
import { TenantMigration } from './tenant-migration.entity';
@Module({
imports: [DataSourceModule],
imports: [TypeOrmModule.forFeature([TenantMigration], 'metadata')],
exports: [TenantMigrationService],
providers: [TenantMigrationService],
})

View File

@ -1,8 +1,8 @@
import { Test, TestingModule } from '@nestjs/testing';
import { DataSourceService } from 'src/metadata/data-source/data-source.service';
import { getRepositoryToken } from '@nestjs/typeorm';
import { TenantMigrationService } from './tenant-migration.service';
import { TenantMigration } from './tenant-migration.entity';
describe('TenantMigrationService', () => {
let service: TenantMigrationService;
@ -12,7 +12,7 @@ describe('TenantMigrationService', () => {
providers: [
TenantMigrationService,
{
provide: DataSourceService,
provide: getRepositoryToken(TenantMigration, 'metadata'),
useValue: {},
},
],

View File

@ -1,17 +1,55 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { IsNull } from 'typeorm';
import { DataSourceService } from 'src/metadata/data-source/data-source.service';
import { IsNull, Repository } from 'typeorm';
import {
TenantMigration,
TenantMigrationTableChange,
TenantMigrationTableAction,
} from './tenant-migration.entity';
import { standardMigrations } from './standard-migrations';
@Injectable()
export class TenantMigrationService {
constructor(private readonly dataSourceService: DataSourceService) {}
constructor(
@InjectRepository(TenantMigration, 'metadata')
private readonly tenantMigrationRepository: Repository<TenantMigration>,
) {}
/**
* Insert all standard migrations that have not been inserted yet
*
* @param workspaceId
*/
public async insertStandardMigrations(workspaceId: string) {
// TODO: we actually don't need to fetch all of them, to improve later so it scales well.
const insertedStandardMigrations =
await this.tenantMigrationRepository.find({
where: { workspaceId, isCustom: false },
});
const insertedStandardMigrationsMapByName =
insertedStandardMigrations.reduce((acc, migration) => {
acc[migration.name] = migration;
return acc;
}, {});
const standardMigrationsList = standardMigrations;
const standardMigrationsListThatNeedToBeInserted = Object.entries(
standardMigrationsList,
)
.filter(([name]) => !insertedStandardMigrationsMapByName[name])
.map(([name, migrations]) => ({ name, migrations }));
await this.tenantMigrationRepository.save(
standardMigrationsListThatNeedToBeInserted.map((migration) => ({
...migration,
workspaceId,
isCustom: false,
})),
);
}
/**
* Get all pending migrations for a given workspaceId
@ -22,19 +60,12 @@ export class TenantMigrationService {
public async getPendingMigrations(
workspaceId: string,
): Promise<TenantMigration[]> {
const workspaceDataSource =
await this.dataSourceService.connectToWorkspaceDataSource(workspaceId);
if (!workspaceDataSource) {
throw new Error('Workspace data source not found');
}
const tenantMigrationRepository =
workspaceDataSource.getRepository(TenantMigration);
return tenantMigrationRepository.find({
return this.tenantMigrationRepository.find({
order: { createdAt: 'ASC' },
where: { appliedAt: IsNull() },
where: {
appliedAt: IsNull(),
workspaceId,
},
});
}
@ -49,17 +80,7 @@ export class TenantMigrationService {
workspaceId: string,
migration: TenantMigration,
) {
const workspaceDataSource =
await this.dataSourceService.connectToWorkspaceDataSource(workspaceId);
if (!workspaceDataSource) {
throw new Error('Workspace data source not found');
}
const tenantMigrationRepository =
workspaceDataSource.getRepository(TenantMigration);
await tenantMigrationRepository.save({
await this.tenantMigrationRepository.save({
id: migration.id,
appliedAt: new Date(),
});
@ -71,22 +92,14 @@ export class TenantMigrationService {
* @param workspaceId
* @param migrations
*/
public async createMigration(
public async createCustomMigration(
workspaceId: string,
migrations: TenantMigrationTableChange[],
migrations: TenantMigrationTableAction[],
) {
const workspaceDataSource =
await this.dataSourceService.connectToWorkspaceDataSource(workspaceId);
if (!workspaceDataSource) {
throw new Error('Workspace data source not found');
}
const tenantMigrationRepository =
workspaceDataSource.getRepository(TenantMigration);
await tenantMigrationRepository.save({
await this.tenantMigrationRepository.save({
migrations,
workspaceId,
isCustom: true,
});
}
}