diff --git a/README.md b/README.md index 4a966d99..ca0259f7 100644 --- a/README.md +++ b/README.md @@ -142,7 +142,8 @@ All script are defined in the package.json file, but the most important ones are ### Database Migration -- Run `./node_modules/.bin/typeorm create -n ` to create a new migration file. +- Run `typeorm migrations:create -n ` to create a new migration file. +- Try `typeorm -h` to see more useful cli commands like generating migration out of your models. - To migrate your database run `npm start db.migrate`. - To revert your latest migration run `npm start db.revert`. - Drops the complete database schema `npm start db.drop`. @@ -267,19 +268,44 @@ factory.define(User, (faker: typeof Faker) => { }); ``` -This is a nested example for a factory to get the foreign key of the other entity. +This can be used to pass some dynamic value into the factory. ```typescript factory.define(Pet, (faker: typeof Faker, args: any[]) => { const type = args[0]; return { name: faker.name.firstName(), - type: type || 'dog', - userId: factory.get(User).returning('id') + type: type || 'dog' }; }); ``` +To deal with relations you can use the entity manager like this. + +```typescript +import { SeedsInterface, FactoryInterface, times } from '../../lib/seeds'; +import { Pet } from '../../../src/api/models/Pet'; +import { User } from '../../../src/api/models/User'; + +export class CreatePets implements SeedsInterface { + + public async seed(factory: FactoryInterface): Promise { + const connection = await factory.getConnection(); + const em = connection.createEntityManager(); + + await times(10, async (n) => { + // This creates a pet in the database + const pet = await factory.get(Pet).create(); + // This only returns a entity with fake data + const user = await factory.get(User).make(); + user.pets = [pet]; + await em.save(user); + }); + } + +} +``` + ### 2. Create a seed file The seeds files define how much and how the data are connected with each other. The files will be executed alphabetically. diff --git a/src/api/controllers/PetController.ts b/src/api/controllers/PetController.ts new file mode 100644 index 00000000..fab67419 --- /dev/null +++ b/src/api/controllers/PetController.ts @@ -0,0 +1,41 @@ +import { JsonController, Get, Post, Put, Param, Delete, Body, OnUndefined, Authorized } from 'routing-controllers'; +import { PetService } from '../services/PetService'; +import { Pet } from '../models/Pet'; +import { PetNotFoundError } from '../errors/PetNotFoundError'; + + +@Authorized() +@JsonController('/pets') +export class PetController { + + constructor( + private petService: PetService + ) { } + + @Get() + public find(): Promise { + return this.petService.find(); + } + + @Get('/:id') + @OnUndefined(PetNotFoundError) + public one( @Param('id') id: string): Promise { + return this.petService.findOne(id); + } + + @Post() + public create( @Body() pet: Pet): Promise { + return this.petService.create(pet); + } + + @Put('/:id') + public update( @Param('id') id: string, @Body() pet: Pet): Promise { + return this.petService.update(id, pet); + } + + @Delete('/:id') + public delete( @Param('id') id: string): Promise { + return this.petService.delete(id); + } + +} diff --git a/src/api/errors/PetNotFoundError.ts b/src/api/errors/PetNotFoundError.ts new file mode 100644 index 00000000..3f443512 --- /dev/null +++ b/src/api/errors/PetNotFoundError.ts @@ -0,0 +1,7 @@ +import { HttpError } from 'routing-controllers'; + +export class PetNotFoundError extends HttpError { + constructor() { + super(404, 'Pet not found!'); + } +} diff --git a/src/api/models/Pet.ts b/src/api/models/Pet.ts new file mode 100644 index 00000000..7556ff5d --- /dev/null +++ b/src/api/models/Pet.ts @@ -0,0 +1,27 @@ +import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from 'typeorm'; +import { IsNotEmpty } from 'class-validator'; +import { User } from './User'; + + +@Entity() +export class Pet { + + @PrimaryGeneratedColumn('uuid') + public id: string; + + @IsNotEmpty() + @Column() + public name: string; + + @IsNotEmpty() + @Column() + public age: number; + + @ManyToOne(type => User, user => user.pets) + public user: User; + + public toString(): string { + return `${this.name}`; + } + +} diff --git a/src/api/models/User.ts b/src/api/models/User.ts index 37cfebda..bb68c85e 100644 --- a/src/api/models/User.ts +++ b/src/api/models/User.ts @@ -1,5 +1,6 @@ -import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm'; +import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm'; import { IsNotEmpty } from 'class-validator'; +import { Pet } from './Pet'; @Entity() @@ -20,6 +21,9 @@ export class User { @Column() public email: string; + @OneToMany(type => Pet, pet => pet.user) + public pets: Pet[]; + public toString(): string { return `${this.firstName} ${this.lastName} (${this.email})`; } diff --git a/src/api/repositories/PetRepository.ts b/src/api/repositories/PetRepository.ts new file mode 100644 index 00000000..77e031ae --- /dev/null +++ b/src/api/repositories/PetRepository.ts @@ -0,0 +1,7 @@ +import { Repository, EntityRepository } from 'typeorm'; +import { Pet } from '../models/Pet'; + +@EntityRepository(Pet) +export class PetRepository extends Repository { + +} diff --git a/src/api/services/PetService.ts b/src/api/services/PetService.ts new file mode 100644 index 00000000..9a063be9 --- /dev/null +++ b/src/api/services/PetService.ts @@ -0,0 +1,47 @@ +import { Service } from 'typedi'; +import { OrmRepository } from 'typeorm-typedi-extensions'; +import { PetRepository } from '../repositories/PetRepository'; +import { Pet } from '../models/Pet'; +import { events } from '../subscribers/events'; +import { EventDispatcher, EventDispatcherInterface } from '../../decorators/EventDispatcher'; +import { Logger, LoggerInterface } from '../../decorators/Logger'; + + +@Service() +export class PetService { + + constructor( + @OrmRepository() private petRepository: PetRepository, + @EventDispatcher() private eventDispatcher: EventDispatcherInterface, + @Logger(__filename) private log: LoggerInterface + ) { } + + public find(): Promise { + this.log.info('Find all pets'); + return this.petRepository.find(); + } + + public findOne(id: string): Promise { + this.log.info('Find all pets'); + return this.petRepository.findOne({ id }); + } + + public async create(pet: Pet): Promise { + this.log.info('Create a new pet => ', pet.toString()); + const newPet = await this.petRepository.save(pet); + this.eventDispatcher.dispatch(events.pet.created, newPet); + return newPet; + } + + public update(id: string, pet: Pet): Promise { + this.log.info('Update a pet'); + pet.id = id; + return this.petRepository.save(pet); + } + + public delete(id: string): Promise { + this.log.info('Delete a pet'); + return this.petRepository.removeById(id); + } + +} diff --git a/src/api/services/UserService.ts b/src/api/services/UserService.ts index addafc3d..6aadf3fb 100644 --- a/src/api/services/UserService.ts +++ b/src/api/services/UserService.ts @@ -18,7 +18,7 @@ export class UserService { public find(): Promise { this.log.info('Find all users'); - return this.userRepository.find(); + return this.userRepository.find({ relations: ['pets'] }); } public findOne(id: string): Promise { diff --git a/src/api/subscribers/events.ts b/src/api/subscribers/events.ts index 2d236326..d9b20bf3 100644 --- a/src/api/subscribers/events.ts +++ b/src/api/subscribers/events.ts @@ -7,4 +7,7 @@ export const events = { user: { created: 'onUserCreate', }, + pet: { + created: 'onPetCreate', + }, }; diff --git a/src/api/swagger.json b/src/api/swagger.json index ddaff6bc..8e04587f 100644 --- a/src/api/swagger.json +++ b/src/api/swagger.json @@ -1,12 +1,12 @@ { "swagger": "2.0", - "host": "localhost:3000", + "host": "", "info": { "title": "", "description": "", "version": "" }, - "basePath": "/api", + "basePath": "", "consumes": [ "application/json" ], @@ -29,6 +29,9 @@ }, { "name": "User" + }, + { + "name": "Pet" } ], "paths": { @@ -186,7 +189,7 @@ { "name": "id", "in": "path", - "description": "ID of user to delete", + "description": "ID of user to update", "required": true, "type": "integer", "format": "int64" @@ -260,6 +263,220 @@ } } } + }, + "/pets": { + "get": { + "tags": [ + "Pet" + ], + "summary": "List all pets", + "description": "Returns all pets", + "operationId": "FindAllPets", + "security": [ + { + "JWT": [] + } + ], + "responses": { + "200": { + "description": "ok", + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "message": { + "type": "string" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + } + } + } + } + }, + "post": { + "tags": [ + "Pet" + ], + "summary": "Create new pet", + "description": "", + "operationId": "CreatePet", + "security": [ + { + "JWT": [] + } + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "User object that needs to be added to the database", + "required": true, + "schema": { + "$ref": "#/definitions/NewPet" + } + } + ], + "responses": { + "201": { + "description": "created", + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "message": { + "type": "string" + }, + "data": { + "$ref": "#/definitions/Pet" + } + } + } + } + } + } + }, + "/pets/{id}": { + "get": { + "tags": [ + "Pet" + ], + "summary": "Get pet", + "description": "Returns the given pet", + "operationId": "FindPet", + "security": [ + { + "JWT": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of pet to return", + "required": true, + "type": "integer", + "format": "int64" + } + ], + "responses": { + "200": { + "description": "ok", + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "message": { + "type": "string" + }, + "data": { + "$ref": "#/definitions/Pet" + } + } + } + } + } + }, + "put": { + "tags": [ + "Pet" + ], + "summary": "Update pet", + "description": "Updates the given pet", + "operationId": "UpdatePet", + "security": [ + { + "JWT": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of pet to update", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "in": "body", + "name": "body", + "description": "Pet object that needs to be added to the database", + "required": true, + "schema": { + "$ref": "#/definitions/NewPet" + } + } + ], + "responses": { + "200": { + "description": "ok", + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "message": { + "type": "string" + }, + "data": { + "$ref": "#/definitions/Pet" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Pet" + ], + "summary": "Delete pet", + "description": "Removes the given pet", + "operationId": "DeletePet", + "security": [ + { + "JWT": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of pet to delete", + "required": true, + "type": "integer", + "format": "int64" + } + ], + "responses": { + "200": { + "description": "ok", + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "message": { + "type": "string" + } + } + } + } + } + } } }, "definitions": { @@ -280,6 +497,22 @@ } } }, + "NewPet": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "age": { + "type": "integer", + "format": "int64" + }, + "userId": { + "type": "integer", + "format": "int64" + } + } + }, "User": { "title": "User", "allOf": [ @@ -304,6 +537,38 @@ } } ] + }, + "Pet": { + "title": "Pet", + "allOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "age": { + "type": "integer", + "format": "int64" + }, + "user": { + "$ref": "#/definitions/User" + }, + "updatedAt": { + "type": "string", + "format": "date-time" + }, + "createdAt": { + "type": "string", + "format": "date-time" + } + } + } + ] } } } diff --git a/src/core/env.ts b/src/core/env.ts index d0a64622..73a6f444 100644 --- a/src/core/env.ts +++ b/src/core/env.ts @@ -22,9 +22,9 @@ export const env = { port: normalizePort(process.env.PORT || '3000'), banner: toBool(getOsEnv('APP_BANNER')), dirs: { - migrations: [path.join(__dirname, '..', 'database/migrations/*.ts')], - migrationsDir: path.join(__dirname, '..', 'database/migrations'), - entities: [path.join(__dirname, '..', 'api/**/models/*{.js,.ts}')], + migrations: [path.relative(path.join(process.cwd()), path.join(__dirname, '..', 'database/migrations/*.ts'))], + migrationsDir: path.relative(path.join(process.cwd()), path.join(__dirname, '..', 'database/migrations')), + entities: [path.relative(path.join(process.cwd()), path.join(__dirname, '..', 'api/**/models/*{.js,.ts}'))], subscribers: [path.join(__dirname, '..', 'api/**/*Subscriber{.js,.ts}')], controllers: [path.join(__dirname, '..', 'api/**/*Controller{.js,.ts}')], middlewares: [path.join(__dirname, '..', 'api/**/*Middleware{.js,.ts}')], diff --git a/src/database/factories/PetFactory.ts b/src/database/factories/PetFactory.ts new file mode 100644 index 00000000..1ff34028 --- /dev/null +++ b/src/database/factories/PetFactory.ts @@ -0,0 +1,19 @@ +import * as Faker from 'faker'; +import { Factory } from '../../lib/seeds'; +import { Pet } from '../../../src/api/models/Pet'; + +const factory = Factory.getInstance(); + + +/** + * Pet factory + */ +factory.define(Pet, (faker: typeof Faker) => { + const gender = faker.random.number(1); + const name = faker.name.firstName(gender); + + const pet = new Pet(); + pet.name = name; + pet.age = faker.random.number(); + return pet; +}); diff --git a/src/database/migrations/1511105183653-CreateUserTable.ts b/src/database/migrations/1511105183653-CreateUserTable.ts index f53eed33..e79b1e1d 100644 --- a/src/database/migrations/1511105183653-CreateUserTable.ts +++ b/src/database/migrations/1511105183653-CreateUserTable.ts @@ -8,12 +8,12 @@ export class CreateUserTable1511105183653 implements MigrationInterface { \`id\` varchar(255) NOT NULL PRIMARY KEY, \`first_name\` varchar(255) NOT NULL, \`last_name\` varchar(255) NOT NULL, - \`email\` varchar(255) NOT NULL) ENGINE=InnoDB` + \`email\` varchar(255) NOT NULL) ENGINE=InnoDB;` ); } public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE \`user\``); + await queryRunner.query(`DROP TABLE \`user\`;`); } } diff --git a/src/database/migrations/1512663524808-CreatePetTable.ts b/src/database/migrations/1512663524808-CreatePetTable.ts new file mode 100644 index 00000000..479459b4 --- /dev/null +++ b/src/database/migrations/1512663524808-CreatePetTable.ts @@ -0,0 +1,19 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class CreatePetTable1512663524808 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + CREATE TABLE \`pet\` ( + \`id\` varchar(255) NOT NULL PRIMARY KEY, + \`name\` varchar(255) NOT NULL, + \`age\` int(11) NOT NULL, + \`userId\` varchar(255)) ENGINE=InnoDB;` + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP TABLE \`pet\`;`); + } + +} diff --git a/src/database/migrations/1512663990063-AddUserRelationToPetTable.ts b/src/database/migrations/1512663990063-AddUserRelationToPetTable.ts new file mode 100644 index 00000000..3ee7be34 --- /dev/null +++ b/src/database/migrations/1512663990063-AddUserRelationToPetTable.ts @@ -0,0 +1,22 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddUserRelationToPetTable1512663990063 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE \`pet\` + ADD CONSTRAINT \`fk_user_pet\` + FOREIGN KEY (\`userId\`) + REFERENCES \`user\`(\`id\`);` + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE \`pet\` + DROP FOREIGN KEY \`fk_user_pet\`;` + ); + } + +} + diff --git a/src/database/seeds/0000-CreateUsers.ts b/src/database/seeds/0000-CreateUsers.ts index b8015321..4d314298 100644 --- a/src/database/seeds/0000-CreateUsers.ts +++ b/src/database/seeds/0000-CreateUsers.ts @@ -7,7 +7,7 @@ export class CreateUsers implements SeedsInterface { public async seed(factory: FactoryInterface): Promise { await factory .get(User) - .create(1); + .create(); } } diff --git a/src/database/seeds/0001-CreatePets.ts b/src/database/seeds/0001-CreatePets.ts new file mode 100644 index 00000000..3a6ba100 --- /dev/null +++ b/src/database/seeds/0001-CreatePets.ts @@ -0,0 +1,20 @@ +import { SeedsInterface, FactoryInterface, times } from '../../lib/seeds'; +import { Pet } from '../../../src/api/models/Pet'; +import { User } from '../../../src/api/models/User'; + + +export class CreatePets implements SeedsInterface { + + public async seed(factory: FactoryInterface): Promise { + const connection = await factory.getConnection(); + const em = connection.createEntityManager(); + + await times(10, async (n) => { + const pet = await factory.get(Pet).create(); + const user = await factory.get(User).make(); + user.pets = [pet]; + await em.save(user); + }); + } + +} diff --git a/src/lib/seeds/BluePrint.ts b/src/lib/seeds/BluePrint.ts index a001b899..0d31ffa9 100644 --- a/src/lib/seeds/BluePrint.ts +++ b/src/lib/seeds/BluePrint.ts @@ -8,5 +8,5 @@ import { ObjectType } from 'typeorm'; export class BluePrint { constructor( public EntityClass: ObjectType, - public callback: (faker: typeof Faker, args: any[]) => any) { } + public create: (faker: typeof Faker, args: any[]) => Entity) { } } diff --git a/src/lib/seeds/EntityFactory.ts b/src/lib/seeds/EntityFactory.ts index 0f2eeab0..839842bf 100644 --- a/src/lib/seeds/EntityFactory.ts +++ b/src/lib/seeds/EntityFactory.ts @@ -1,54 +1,51 @@ import * as Faker from 'faker'; -import { Connection } from 'typeorm'; +import { Connection } from 'typeorm/connection/Connection'; import { EntityFactoryInterface } from './EntityFactoryInterface'; import { BluePrint } from './BluePrint'; -import { getConnection } from './connection'; export class EntityFactory implements EntityFactoryInterface { - private connection: Connection | undefined; private identifier = 'id'; private eachFn: (obj: any, faker: typeof Faker) => Promise; constructor( private faker: typeof Faker, + private connection: Connection, private blueprint: BluePrint, private args: any[]) { } - public returning(identifier: string): EntityFactory { - this.identifier = identifier; - return this; - } - public each(iterator: (entity: Entity, faker: typeof Faker) => Promise): EntityFactory { this.eachFn = iterator; return this; } - public async create(amount: number = 1): Promise { - this.connection = await getConnection(); - if (this.connection) { - const results: Entity[] = []; - for (let i = 0; i < amount; i++) { - const entity = await this.build(); + public async make(): Promise { + return await this.makeEntity(this.blueprint.create(this.faker, this.args)); + } + + public async create(): Promise { + const entity = await this.build(); + if (typeof this.eachFn === 'function') { + await this.eachFn(entity, this.faker); + } + return entity; + } + + public async createMany(amount: number): Promise { + const results: Entity[] = []; + for (let i = 0; i < amount; i++) { + const entity = await this.create(); + if (entity) { results.push(entity); - if (typeof this.eachFn === 'function') { - await this.eachFn(entity, this.faker); - } - } - await this.connection.close(); - if (amount === 1) { - return results[0]; } - return results; } - return; + return results; } private async build(): Promise { if (this.connection) { - const entity = await this.makeEntity(this.blueprint.callback(this.faker, this.args)); + const entity = await this.make(); const em = this.connection.createEntityManager(); try { return await em.save(this.blueprint.EntityClass, entity); diff --git a/src/lib/seeds/EntityFactoryInterface.ts b/src/lib/seeds/EntityFactoryInterface.ts index 850342fa..10fbaf92 100644 --- a/src/lib/seeds/EntityFactoryInterface.ts +++ b/src/lib/seeds/EntityFactoryInterface.ts @@ -1,18 +1,23 @@ import * as Faker from 'faker'; + /** * EntityFactoryInterface is the one we use in our seed files. * This will be returne of the main factory's get method. */ export interface EntityFactoryInterface { /** - * Creates an amount (default 1) of the defined entity. + * Creates a entity with faked data, but not persisted to the database. */ - create(amount: number): Promise; + make(): Promise; /** - * Returns the identifier of the created entity. + * Creates a new faked entity in the database. + */ + create(): Promise; + /** + * Creates an amount (default 1) of the defined entity. */ - returning(identifier: string): EntityFactoryInterface; + createMany(amount: number): Promise; /** * This is called after creating a enity to the database. Use this to * create other seeds but combined with this enitiy. diff --git a/src/lib/seeds/Factory.ts b/src/lib/seeds/Factory.ts index 0f39903d..76155ee6 100644 --- a/src/lib/seeds/Factory.ts +++ b/src/lib/seeds/Factory.ts @@ -1,5 +1,6 @@ -import { ObjectType } from 'typeorm'; import * as Faker from 'faker'; +import { ObjectType } from 'typeorm'; +import { Connection } from 'typeorm/connection/Connection'; import { FactoryInterface } from './FactoryInterface'; import { EntityFactory } from './EntityFactory'; import { BluePrint } from './BluePrint'; @@ -16,12 +17,21 @@ export class Factory implements FactoryInterface { private static instance: Factory; + private connection: Connection; private blueprints: { [key: string]: BluePrint }; constructor(private faker: typeof Faker) { this.blueprints = {}; } + public getConnection(): Connection { + return this.connection; + } + + public setConnection(connection: Connection): void { + this.connection = connection; + } + public define(entityClass: ObjectType, callback: (faker: typeof Faker, args: any[]) => Entity): void { this.blueprints[this.getNameOfEntity(entityClass)] = new BluePrint(entityClass, callback); } @@ -29,6 +39,7 @@ export class Factory implements FactoryInterface { public get(entityClass: ObjectType, ...args: any[]): EntityFactory { return new EntityFactory( this.faker, + this.connection, this.blueprints[this.getNameOfEntity(entityClass)], args ); diff --git a/src/lib/seeds/FactoryInterface.ts b/src/lib/seeds/FactoryInterface.ts index b4796081..5f25bc39 100644 --- a/src/lib/seeds/FactoryInterface.ts +++ b/src/lib/seeds/FactoryInterface.ts @@ -1,11 +1,20 @@ import * as Faker from 'faker'; import { ObjectType } from 'typeorm'; import { EntityFactoryInterface } from './EntityFactoryInterface'; +import { Connection } from 'typeorm/connection/Connection'; /** * This interface is used to define new entity faker factories or to get such a * entity faker factory to start seeding. */ export interface FactoryInterface { + /** + * Returns a typeorm database connection. + */ + getConnection(): Connection; + /** + * Sets the typeorm database connection. + */ + setConnection(connection: Connection): void; /** * Returns an EntityFactoryInterface */ diff --git a/src/lib/seeds/SeedsInterface.ts b/src/lib/seeds/SeedsInterface.ts index 80b26628..81bf2397 100644 --- a/src/lib/seeds/SeedsInterface.ts +++ b/src/lib/seeds/SeedsInterface.ts @@ -7,5 +7,4 @@ export interface SeedsInterface { * Seed data into the databas. */ seed(factory: FactoryInterface): Promise; - } diff --git a/src/lib/seeds/connection.ts b/src/lib/seeds/connection.ts index 37c1f526..db2d1c7e 100644 --- a/src/lib/seeds/connection.ts +++ b/src/lib/seeds/connection.ts @@ -14,28 +14,20 @@ const indexOfConfigPath = args.indexOf(configParam) + 1; /** * Returns a TypeORM database connection for our entity-manager */ -export const getConnection = async (): Promise => { +export const getConnection = async (): Promise => { const ormconfig = (hasConfigPath) ? require(`${args[indexOfConfigPath]}`) : require(`${runDir}/ormconfig.json`); - try { - const connection = await createConnection({ - type: (ormconfig as any).type as any, - host: (ormconfig as any).host, - port: (ormconfig as any).port, - username: (ormconfig as any).username, - password: (ormconfig as any).password, - database: (ormconfig as any).database, - entities: (ormconfig as any).entities, - logging, - }); - return connection; - } catch (error) { - console.error(error); - return; - } + return await createConnection({ + type: (ormconfig as any).type as any, + host: (ormconfig as any).host, + port: (ormconfig as any).port, + username: (ormconfig as any).username, + password: (ormconfig as any).password, + database: (ormconfig as any).database, + entities: (ormconfig as any).entities, + logging, + }); }; - - diff --git a/src/lib/seeds/index.ts b/src/lib/seeds/index.ts index 9aae518c..7336ebd3 100644 --- a/src/lib/seeds/index.ts +++ b/src/lib/seeds/index.ts @@ -3,7 +3,10 @@ import * as path from 'path'; import * as glob from 'glob'; import * as commander from 'commander'; import * as Chalk from 'chalk'; +import { Connection } from 'typeorm'; import { Factory } from './Factory'; +import { getConnection } from './connection'; + // Get executiuon path to look from there for seeds and factories const runDir = process.cwd(); @@ -44,32 +47,42 @@ glob(path.join(runDir, factoryPath, '**/*Factory{.js,.ts}'), (errFactories: any, require(factory); } - // Initialize and seed all seeds. - const queue: Array> = []; - for (const seed of seeds) { - try { - const seedFile: any = require(seed); - let className = seed.split('/')[seed.split('/').length - 1]; - className = className.replace('.ts', '').replace('.js', ''); - className = className.split('-')[className.split('-').length - 1]; - log('\n' + chalk.gray.underline(`executing seed: `), chalk.green.bold(`${className}`)); - queue.push((new seedFile[className]()).seed(Factory.getInstance())); - } catch (error) { - console.error('Could not run seed ' + seed, error); + // Get typeorm database connection and pass them to the factory instance + getConnection().then((connection: Connection) => { + const factory = Factory.getInstance(); + factory.setConnection(connection); + + // Initialize and seed all seeds. + const queue: Array> = []; + for (const seed of seeds) { + try { + const seedFile: any = require(seed); + let className = seed.split('/')[seed.split('/').length - 1]; + className = className.replace('.ts', '').replace('.js', ''); + className = className.split('-')[className.split('-').length - 1]; + log('\n' + chalk.gray.underline(`executing seed: `), chalk.green.bold(`${className}`)); + queue.push((new seedFile[className]()).seed(factory)); + } catch (error) { + console.error('Could not run seed ' + seed, error); + } } - } - // Promise to catch the end for termination and logging - Promise - .all(queue) - .then(() => { - log('\nšŸ‘ ', chalk.gray.underline(`finished seeding`)); - process.exit(0); - }) - .catch((error) => { - console.error('Could not run seed ' + error); - process.exit(1); - }); + // Promise to catch the end for termination and logging + Promise + .all(queue) + .then(() => { + log('\nšŸ‘ ', chalk.gray.underline(`finished seeding`)); + process.exit(0); + }) + .catch((error) => { + console.error('Could not run seed ' + error); + process.exit(1); + }); + + }).catch((error) => { + console.error('Could not connection to database ' + error); + process.exit(1); + }); }); }); @@ -77,3 +90,4 @@ export * from './FactoryInterface'; export * from './EntityFactoryInterface'; export * from './SeedsInterface'; export * from './Factory'; +export * from './utils'; diff --git a/src/lib/seeds/utils.ts b/src/lib/seeds/utils.ts new file mode 100644 index 00000000..5106607e --- /dev/null +++ b/src/lib/seeds/utils.ts @@ -0,0 +1,13 @@ +/** + * Times repeats a function n times + * @param n amount of loops + * @param iteratee this function gets n-times called + */ +export const times = async (n: number, iteratee: (index: number) => Promise): Promise => { + const rs = [] as TResult[]; + for (let i = 0; i < n; i++) { + const r = await iteratee(i); + rs.push(r); + } + return rs; +}; diff --git a/src/loaders/swaggerLoader.ts b/src/loaders/swaggerLoader.ts index c1f4516e..3fadca29 100644 --- a/src/loaders/swaggerLoader.ts +++ b/src/loaders/swaggerLoader.ts @@ -16,6 +16,8 @@ export const swaggerLoader: MicroframeworkLoader = (settings: MicroframeworkSett description: env.app.description, version: env.app.version, }; + swaggerFile.host = env.app.route; + swaggerFile.basePath = env.app.routePrefix; expressApp.use( env.swagger.route,