All files / libs/kernel/bootstrap/database/src/lib AmaliaPostgresQueryRunner.ts

96.66% Statements 58/60
100% Branches 5/5
100% Functions 4/4
96.66% Lines 58/60

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 611x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x     1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x  
import { Logger } from '@nestjs/common';
import { Table, type DataSource, type ObjectType, type QueryRunner, type TableIndex } from 'typeorm';
 
// Cannot import Query nor PostgresQueryRunner directly because they are not exposed in typeorm's module.
type Query = { query: string; parameters?: unknown[] };
 
interface PostgresQueryRunnerLike extends QueryRunner {
  createTableSql: (table: Table) => Query;
  createIndexSql: (table: Table, index: TableIndex) => Query;
  executeQueries: (queries: Query[], _?: unknown) => Promise<void>;
}
 
export class AmaliaPostgresQueryRunner {
  private readonly logger = new Logger(AmaliaPostgresQueryRunner.name);
 
  private readonly queryRunner: PostgresQueryRunnerLike;
 
  public constructor(private readonly currentConnection: DataSource) {
    this.queryRunner = currentConnection.driver.createQueryRunner('master') as PostgresQueryRunnerLike;
  }
 
  private generateTable(entity: ObjectType<unknown>, schema: string) {
    const table = Table.create(this.currentConnection.getMetadata(entity), this.currentConnection.driver);
    table.schema = schema;
    return table;
  }
 
  protected generateSqlForTable(table: Table): Query[] {
    const queries: Query[] = [this.queryRunner.createTableSql(table)];
 
    table.indices.forEach((index) => {
      index.name ||= this.queryRunner.connection.namingStrategy.indexName(table, index.columnNames, index.where);
      queries.push(this.queryRunner.createIndexSql(table, index));
    });
 
    return queries;
  }
 
  public async createTableInCompanySchema(
    targetDataSource: DataSource,
    entity: ObjectType<unknown>,
    schema: string,
    options: { dropExistingFirst?: boolean } = {},
  ) {
    const table = this.generateTable(entity, schema);
 
    if (options.dropExistingFirst) {
      // CASCADE is mandatory here because views are based on it.
      await targetDataSource.query(`DROP TABLE IF EXISTS "${schema}"."${table.name}" CASCADE;`);
    }
 
    const queries = this.generateSqlForTable(table);
 
    this.logger.debug({
      sql: queries.map(({ query }) => query),
    });
 
    await this.queryRunner.executeQueries(queries, []);
  }
}