Skip to content

Commit

Permalink
Add auto persist option
Browse files Browse the repository at this point in the history
  • Loading branch information
pashak09 authored Sep 14, 2023
2 parents fb19d3f + 5e10854 commit 008a016
Show file tree
Hide file tree
Showing 18 changed files with 1,211 additions and 864 deletions.
71 changes: 60 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,38 +13,87 @@
</p>

### Installation

```bash
yarn add -D typeorm-fixturio
```

### Options

| Option | Short | Type | Description |
| ---------------- | ----- | -------- |---------------------------------------------------------------|
| recreate | | boolean | Determines whether to recreate the database |
| runMigration | | boolean | Determines whether to run the migration |
| quiet | | boolean | Suppresses console output during execution |
| dataSourceFile | -d | string | Path to the orm data source file |
| containerFile | | string | Path to the container file |
| filePatterns | | string[] | Array of fixture patterns to match and process multiple files |
| Option | Short | Type | Description |
|----------------|-------|----------|---------------------------------------------------------------|
| recreate | | boolean | Determines whether to recreate the database |
| runMigration | | boolean | Determines whether to run the migration |
| quiet | | boolean | Suppresses console output during execution |
| autoPersist | | boolean | Automatic entity saving |
| dataSourceFile | -d | string | Path to the orm data source file |
| containerFile | | string | Path to the container file |
| filePatterns | | string[] | Array of fixture patterns to match and process multiple files |

For `autoPersist` flag you don't need to save entities manually you need just return the entities for saving in fixture'
s
install method. Notice that entity resolver can work only with a flat object and an array returned types. An example

```ts
async install(): Promise < UserFixtureResultOf > {
const tag1 = new Tag('tag_1');
const tag2 = new Tag('tag_1');

return {tag1, tag1}
//or
return [tag2, tag2]
}
```

if `autoPersist` option is disabled the entities have to be saved manually. EntityManager is provided by default(configured
from the `dataSourceFile` option).

```ts
import { FixtureInterface } from 'fixturio';
import { EntityManager } from 'typeorm';
import { User } from '../entity/User';

type UserFixtureResultOf = {
readonly firstUser: User;
};

export class UserFixture implements FixtureInterface<UserFixtureResultOf>, DependencyInjectable {
constructor(private readonly entityManager: EntityManager) {}

getInjectDependencies(): readonly InjectDependency[] {
return [EntityManager];
}

async install(): Promise<UserFixtureResultOf> {
const firstUser = new User(1, 'user_1');

await this.entityManager.save([firstUser]);

return {
firstUser,
};
}
}
```

An example how to run cli command

```json
//package.json
...
"scripts": {
"prepare-database": "node -r ts-node/register -r tsconfig-paths/register ./node_modules/typeorm-fixturio/dist/cli.js -d ormconfig.ts --filePatterns 'tests/fixtures/**/*.ts' --recreate --runMigration"
"prepare-database": "node -r ts-node/register -r tsconfig-paths/register ./node_modules/typeorm-fixturio/dist/cli.js -d ormconfig.ts --filePatterns 'tests/fixtures/**/*.ts' --recreate --runMigration"
}
//...
```

This library is based on https://github.com/pashak09/fixturio.
This library is based on https://github.com/pashak09/fixturio

### Examples

Examples of usage can be found in <a href="https://github.com/pashak09/typeorm-fixturio/tree/master/examples">examples</a> folder
Examples of usage can be found in <a href="https://github.com/pashak09/typeorm-fixturio/tree/master/examples">
examples</a> folder

### License

MIT
8 changes: 4 additions & 4 deletions examples/entity/Article.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { User } from './User';
@Entity()
export class Article {
@PrimaryGeneratedColumn()
// @ts-expect-error - TS2564: Property `id` as no initializer and is not definitely assigned in the constructor.
id: number;

@Column()
Expand All @@ -20,15 +21,14 @@ export class Article {
@CreateDateColumn()
createdAt: Date;

@ManyToOne(() => User, { nullable: false })
@ManyToOne(() => User, { nullable: false, lazy: true })
@JoinColumn()
author: User;
author: User | Promise<User>;

@Column({ default: 0 })
likeCount: number;

constructor(id: number, author: User, content: string, createdAt: Date) {
this.id = id;
constructor(author: User, content: string, createdAt: Date) {
this.author = author;
this.content = content;
this.createdAt = createdAt;
Expand Down
15 changes: 15 additions & 0 deletions examples/entity/Tag.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class Tag {
@PrimaryGeneratedColumn()
// @ts-expect-error - TS2564: Property id has no initializer and is not definitely assigned in the constructor.
id: number;

@Column()
description: string;

constructor(description: string) {
this.description = description;
}
}
4 changes: 2 additions & 2 deletions examples/entity/User.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export class User {
@PrimaryGeneratedColumn()
// @ts-expect-error - TS2564: Property `id` as no initializer and is not definitely assigned in the constructor.
id: number;

@Column()
name: string;

constructor(id: number, name: string) {
this.id = id;
constructor(name: string) {
this.name = name;
}
}
5 changes: 3 additions & 2 deletions examples/entity/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { User } from './User';
import { Article } from './Article';
import { Tag } from './Tag';
import { User } from './User';

export const ENTITIES = [Article, User];
export const ENTITIES = [Article, User, Tag];
20 changes: 3 additions & 17 deletions examples/fixtures/ArticleFixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,8 @@ import {
DependentFixtureInterface,
FixtureInterface,
FixtureBucket,
DependencyInjectable,
FixtureDependency,
InjectDependency,
} from 'fixturio';
import { EntityManager } from 'typeorm';

import { Article } from '../entity/Article';

Expand All @@ -18,28 +15,17 @@ type ArticleFixtureResultOf = {
};

export class ArticleFixture
implements
FixtureInterface<ArticleFixtureResultOf>,
DependentFixtureInterface,
DependencyInjectable
implements FixtureInterface<ArticleFixtureResultOf>, DependentFixtureInterface
{
constructor(private readonly entityManager: EntityManager) {}

getInjectDependencies(): readonly InjectDependency[] {
return [EntityManager];
}

getFixtureDependencies(): readonly FixtureDependency[] {
return [UserFixture];
}

async install(fixtureBucket: FixtureBucket): Promise<ArticleFixtureResultOf> {
const { firstUser, secondUser } = fixtureBucket.fixtureResultOf(UserFixture);

const firstArticle = new Article(1, firstUser, 'hey', new Date('2023-01-02T14:33:48.027Z'));
const secondArticle = new Article(2, secondUser, 'hey', new Date('2023-02-02T14:33:48.027Z'));

await this.entityManager.save([firstArticle, secondArticle]);
const firstArticle = new Article(firstUser, 'hey', new Date('2023-01-02T14:33:48.027Z'));
const secondArticle = new Article(secondUser, 'hey', new Date('2023-02-02T14:33:48.027Z'));

return {
firstArticle,
Expand Down
11 changes: 11 additions & 0 deletions examples/fixtures/TagFixture.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { FixtureInterface } from 'fixturio';

import { Tag } from '../entity/Tag';

type TagFixtureResultOf = readonly Tag[];

export class TagFixture implements FixtureInterface<TagFixtureResultOf> {
async install(): Promise<TagFixtureResultOf> {
return [new Tag('tag_1'), new Tag('tag_2')];
}
}
17 changes: 4 additions & 13 deletions examples/fixtures/UserFixture.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { DependencyInjectable, FixtureInterface, InjectDependency } from 'fixturio';
import { EntityManager } from 'typeorm';
import { FixtureInterface } from 'fixturio';

import { User } from '../entity/User';

Expand All @@ -8,18 +7,10 @@ type UserFixtureResultOf = {
readonly secondUser: User;
};

export class UserFixture implements FixtureInterface<UserFixtureResultOf>, DependencyInjectable {
constructor(private readonly entityManager: EntityManager) {}

getInjectDependencies(): readonly InjectDependency[] {
return [EntityManager];
}

export class UserFixture implements FixtureInterface<UserFixtureResultOf> {
async install(): Promise<UserFixtureResultOf> {
const firstUser = new User(1, 'user_1');
const secondUser = new User(2, 'user_2');

await this.entityManager.save([firstUser, secondUser]);
const firstUser = new User('user_1');
const secondUser = new User('user_2');

return {
firstUser,
Expand Down
15 changes: 15 additions & 0 deletions examples/migrations/1111314965021-add-tags.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { MigrationInterface, QueryRunner } from 'typeorm';

export class AddTags1111314965021 implements MigrationInterface {
name: string = 'AddTags1111314965021';

async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE \`tag\` (\`id\` int NOT NULL AUTO_INCREMENT, \`description\` varchar(255) NOT NULL, PRIMARY KEY (\`id\`)) ENGINE=InnoDB`
);
}

async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP TABLE \`tag\``);
}
}
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"tests:coverage": "jest --coverage"
},
"dependencies": {
"fixturio": "^0.0.9",
"fixturio": "^1.0.2",
"typeorm-extension": "^2.8.1"
},
"peerDependencies": {
Expand All @@ -50,7 +50,7 @@
"ts-jest": "^29.0.3",
"ts-node": "^10.9.1",
"tsc-alias": "^1.8.6",
"typeorm": "^0.3.11",
"typeorm": "^0.3.17",
"typescript": "^5.1.3"
},
"engines": {
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from 'fixturio';
export * from './container/TypeOrmContainer';
17 changes: 17 additions & 0 deletions src/orm/entityResolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { isObject } from '@app/utils/isObject';

export function entityResolver(array: readonly unknown[]): readonly unknown[] {
const result: unknown[] = [];

for (const data of array) {
if (Array.isArray(data)) {
result.push(...data);
}

if (isObject(data)) {
result.push(...Object.values(data));
}
}

return result;
}
33 changes: 23 additions & 10 deletions src/runner.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { resolve } from 'node:path';
import { cwd } from 'node:process';

import { containerImporter } from '@app/container/containerImporter';
import { TypeOrmContainer } from '@app/container/TypeOrmContainer';
import { Logger, LogLevel } from '@app/logger/Logger';
import { entityResolver } from '@app/orm/entityResolver';
import { FixtureContainer } from 'fixturio';
import { CommandUtils } from 'typeorm/commands/CommandUtils';
import { checkDatabase, createDatabase, dropDatabase } from 'typeorm-extension';
Expand All @@ -11,6 +13,7 @@ export type Options = {
readonly recreate: boolean;
readonly runMigration: boolean;
readonly quiet: boolean;
readonly autoPersist?: boolean | undefined;
readonly containerFile?: string | undefined;
readonly dataSourceFile: string;
readonly filePatterns: readonly string[];
Expand All @@ -20,12 +23,13 @@ export const runner = async ({
dataSourceFile,
recreate,
runMigration,
autoPersist,
filePatterns,
containerFile,
quiet,
}: Options): Promise<void> => {
const logger = new Logger(quiet ? LogLevel.ERROR : LogLevel.INFO);
const dataSource = await CommandUtils.loadDataSource(resolve(process.cwd(), dataSourceFile));
const dataSource = await CommandUtils.loadDataSource(resolve(cwd(), dataSourceFile));

const databaseName =
'replication' in dataSource.options && 'database' in dataSource.options.replication.master
Expand Down Expand Up @@ -58,18 +62,27 @@ export const runner = async ({
logger.info('Start loading fixtures');

try {
const fixtureContainer = new FixtureContainer({
const fixtureContainer = new FixtureContainer(
containerFile !== undefined
? await containerImporter(containerFile)
: new TypeOrmContainer(dataSource)
);

const { loadedResults } = await fixtureContainer.installFixtures({
filePatterns,
serviceContainer:
containerFile !== undefined
? await containerImporter(containerFile)
: new TypeOrmContainer(dataSource),
rootDir: cwd(),
});

await fixtureContainer.loadFiles();
await fixtureContainer.installFixtures();
} catch (err) {
throw err;
if (autoPersist === true) {
//it's important to save the fixtures in the right order
await dataSource.manager.transaction(async (): Promise<void> => {
for (const entity of entityResolver(loadedResults)) {
await dataSource.manager.save(entity);
}
});
}
} catch (error: unknown) {
throw error;
} finally {
await dataSource.destroy();
}
Expand Down
Loading

0 comments on commit 008a016

Please sign in to comment.