Skip to content

Commit

Permalink
fix: adjust runSeeder(s) method to use seeder executor
Browse files Browse the repository at this point in the history
  • Loading branch information
tada5hi committed Jul 16, 2023
1 parent 0e4bbc0 commit 3f55d49
Show file tree
Hide file tree
Showing 10 changed files with 236 additions and 263 deletions.
2 changes: 1 addition & 1 deletion src/cli/commands/seed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export class SeedCommand implements CommandModule {

const dataSource = await useDataSource();
const executor = new SeederExecutor(dataSource);
await executor.execute(args.seed);
await executor.execute({ seedName: args.seed });

if (exitProcess) {
process.exit(0);
Expand Down
37 changes: 2 additions & 35 deletions src/data-source/options/module.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { DataSourceOptions } from 'typeorm';
import { useEnv } from '../../env';
import { OptionsError } from '../../errors';
import type { SeederOptions } from '../../seeder';
import { extendSeederOptions } from '../../seeder';
import { findDataSource } from '../find';
import type { DataSourceOptionsBuildContext } from './type';
import {
Expand All @@ -10,43 +9,11 @@ import {
readDataSourceOptionsFromEnv,
} from './utils';

export function extendDataSourceOptionsWithSeederOptions<T extends Partial<DataSourceOptions> & SeederOptions>(options: T): T {
if (
!Array.isArray(options.factories) ||
options.factories.length === 0
) {
let factories = useEnv('factories');
if (factories.length === 0) {
factories = ['src/database/factories/**/*{.ts,.js}'];
}

Object.assign(options, {
factories,
});
}

if (
!Array.isArray(options.seeds) ||
options.seeds.length === 0
) {
let seeds = useEnv('seeds');
if (seeds.length === 0) {
seeds = ['src/database/seeds/**/*{.ts,.js}'];
}

Object.assign(options, {
seeds,
});
}

return options;
}

export async function extendDataSourceOptions(
options: DataSourceOptions,
tsConfigDirectory?: string,
) : Promise<DataSourceOptions> {
options = extendDataSourceOptionsWithSeederOptions(options);
options = extendSeederOptions(options);

await adjustFilePathsForDataSourceOptions(options, { root: tsConfigDirectory });

Expand Down
20 changes: 13 additions & 7 deletions src/seeder/entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,24 @@ import type { Seeder, SeederConstructor } from './type';

export class SeederEntity {
/**
* Seeder id.
* Indicates order of the executed migrations.
* ID of the seeder.
*
* Indicates order of the executed seeders.
*/
id?: number;

/**
* Timestamp of the seed.
* Timestamp of the seeder.
*/
timestamp: number;

/**
* Name of the seed (class name).
* Name of the seeder (class name).
*/
name: string;

/**
* Constructor that needs to be run.
* Instance of seeder constructor.
*/
instance?: Seeder;

Expand All @@ -27,6 +28,11 @@ export class SeederEntity {
*/
fileName?: string;

/**
* Result of the executed seeder.
*/
result?: unknown;

constructor(ctx: {
id?: number,
timestamp: number,
Expand All @@ -45,8 +51,8 @@ export class SeederEntity {
this.fileName = ctx.fileName;
}

isOneTime() {
return this.instance &&
isOneTime() : boolean {
return !!this.instance &&
typeof this.instance.oneTimeOnly === 'boolean' &&
this.instance.oneTimeOnly;
}
Expand Down
89 changes: 61 additions & 28 deletions src/seeder/executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import type { ObjectLiteral } from 'rapiq';
import { MssqlParameter, Table } from 'typeorm';
import type { DataSource, DataSourceOptions, QueryRunner } from 'typeorm';
import type { MongoQueryRunner } from 'typeorm/driver/mongodb/MongoQueryRunner';
import { setDataSource } from '../data-source';
import { adjustFilePathsForDataSourceOptions, setDataSource } from '../data-source';
import { SeederEntity } from './entity';
import { useSeederFactoryManager } from './factory';
import { prepareSeeder } from './module';
import type { SeederOptions } from './type';
import type { SeederOptions, SeederPrepareElement } from './type';
import { extendSeederOptions, prepareSeederFactories, prepareSeederSeeds } from './utils';

export class SeederExecutor {
protected dataSource : DataSource;
Expand All @@ -21,14 +21,32 @@ export class SeederExecutor {
this.tableName = this.options.seedTableName || 'seeds';
}

async execute(seedName?: string) : Promise<SeederEntity[]> {
async execute(input: SeederOptions = {}) : Promise<SeederEntity[]> {
const queryRunner = this.dataSource.createQueryRunner();
await this.createTableIfNotExist(queryRunner);

const options = await this.buildOptions(input);
if (!options.seeds || options.seeds.length === 0) {
return [];
}

if (options.factories) {
await prepareSeederFactories(options.factories);
}

const seederElements = await prepareSeederSeeds(options.seeds);

const existing = await this.loadExisting(queryRunner);
const all = await this.loadAll(seedName);
const all = await this.buildEntities(seederElements);

const pending = all.filter((seed) => {
if (
options.seedName &&
options.seedName !== seed.name
) {
return false;
}

const index = existing.findIndex(
(el) => el.name === seed.name,
);
Expand Down Expand Up @@ -60,7 +78,7 @@ export class SeederExecutor {
continue;
}

await seeder.run(this.dataSource, factoryManager);
pending[i].result = await seeder.run(this.dataSource, factoryManager);

await this.track(queryRunner, pending[i]);

Expand Down Expand Up @@ -105,16 +123,11 @@ export class SeederExecutor {
/**
* Gets all migrations that setup for this connection.
*/
protected async loadAll(seedName?: string): Promise<SeederEntity[]> {
if (!this.options.seeds) {
protected async buildEntities(seeds?: SeederPrepareElement[]): Promise<SeederEntity[]> {
if (!seeds) {
return [];
}

const seeds = await prepareSeeder({
...this.options,
...(seedName ? { seedName } : {}),
});

let timestampCounter = 0;
const entities = seeds.map((element) => {
const {
Expand All @@ -129,10 +142,7 @@ export class SeederExecutor {
const className = seed.name || (seed.constructor as any).name;

if (!timestamp) {
const match = className.match(/^(.*)([0-9]{13,})$/);
if (match) {
[,, timestamp] = match;
}
timestamp = this.classNameToTimestamp(className);
}

const entity = new SeederEntity({
Expand All @@ -147,7 +157,7 @@ export class SeederExecutor {
return entity;
});

this.checkForDuplicateMigrations(entities);
this.checkForDuplicates(entities);

// sort them by file name than by timestamp
return entities.sort((a, b) => {
Expand All @@ -162,7 +172,7 @@ export class SeederExecutor {
});
}

protected checkForDuplicateMigrations(entities: SeederEntity[]) {
protected checkForDuplicates(entities: SeederEntity[]) {
const names = entities.map((migration) => migration.name);
const duplicates = Array.from(
new Set(
Expand Down Expand Up @@ -223,15 +233,6 @@ export class SeederExecutor {
}
}

protected getLatest(
migrations: SeederEntity[],
): SeederEntity | undefined {
const entities = migrations
.map((migration) => migration)
.sort((a, b) => (a.timestamp - b.timestamp) * -1);
return entities.length > 0 ? entities[0] : undefined;
}

protected async track(
queryRunner: QueryRunner,
migration: SeederEntity,
Expand Down Expand Up @@ -291,4 +292,36 @@ export class SeederExecutor {
this.database,
);
}

protected async buildOptions(input: SeederOptions = {}) {
let options : SeederOptions = {
seeds: input.seeds || [],
factories: input.factories || [],
};

if (!options.seeds || options.seeds.length === 0) {
options.seeds = this.options.seeds;
}

if (!options.factories || options.factories.length === 0) {
options.factories = this.options.factories;
}

options = extendSeederOptions(options);

await adjustFilePathsForDataSourceOptions(options, {
keys: ['seeds', 'factories'],
});

return options;
}

protected classNameToTimestamp(className: string) {
const match = className.match(/^(.*)([0-9]{13,})$/);
if (match) {
return parseInt(match[2], 10);
}

return undefined;
}
}
Loading

0 comments on commit 3f55d49

Please sign in to comment.