Skip to content

Commit

Permalink
Merge pull request #504 from nestjsx/fix/relations
Browse files Browse the repository at this point in the history
fix(typeorm): updated joining relations and column identifier
  • Loading branch information
michaelyali authored May 7, 2020
2 parents 7ef9744 + ad8d5f2 commit 23f5029
Show file tree
Hide file tree
Showing 15 changed files with 420 additions and 253 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ before_install:
- sudo mv docker-compose /usr/local/bin

install:
- docker-compose up -d
- docker-compose up -d
- yarn bootstrap
- yarn clean
Expand Down
10 changes: 10 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,16 @@ services:
networks:
- nestjsx_crud

mysql:
image: mysql:5.7
ports:
- 3316:3306
environment:
MYSQL_DATABASE: nestjsx_crud
MYSQL_USER: nestjsx_crud
MYSQL_PASSWORD: nestjsx_crud
MYSQL_ROOT_PASSWORD: nestjsx_crud

redis:
image: redis:alpine
ports:
Expand Down
23 changes: 20 additions & 3 deletions integration/crud-typeorm/companies/companies.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,27 @@ import { serialize } from './response';
},
},
query: {
alwaysPaginate: true,
alwaysPaginate: false,
allow: ['name'],
join: {
users: {},
projects: {},
users: {
alias: 'companyUsers',
exclude: ['email'],
eager: true,
},
'users.projects': {
eager: true,
alias: 'usersProjects',
allow: ['name'],
},
'users.projects.company': {
eager: true,
alias: 'usersProjectsCompany',
},
projects: {
eager: true,
select: false,
},
},
},
})
Expand Down
2 changes: 1 addition & 1 deletion integration/crud-typeorm/companies/companies.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { TypeOrmCrudService } from '@nestjsx/crud-typeorm';
import { TypeOrmCrudService } from '../../../packages/crud-typeorm/src/typeorm-crud.service';

import { Company } from './company.entity';

Expand Down
10 changes: 6 additions & 4 deletions integration/crud-typeorm/orm.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import { join } from 'path';
import { TypeOrmModuleOptions } from '@nestjs/typeorm';
import { isNil } from '@nestjsx/util';

const type = (process.env.TYPEORM_CONNECTION as any) || 'postgres';

export const withCache: TypeOrmModuleOptions = {
type: 'postgres',
type,
host: '127.0.0.1',
port: 5455,
username: 'root',
password: 'root',
port: type === 'postgres' ? 5455 : 3316,
username: type === 'mysql' ? 'nestjsx_crud' : 'root',
password: type === 'mysql' ? 'nestjsx_crud' : 'root',
database: 'nestjsx_crud',
synchronize: false,
logging: !isNil(process.env.TYPEORM_LOGGING)
Expand Down
12 changes: 12 additions & 0 deletions integration/crud-typeorm/orm.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,15 @@ default:
migrationsTableName: orm_migrations
migrations:
- ./seeds.ts
mysql:
type: mysql
host: 127.0.0.1
port: 3316
username: nestjsx_crud
password: nestjsx_crud
database: nestjsx_crud
entities:
- ./**/*.entity.ts
migrationsTableName: orm_migrations
migrations:
- ./seeds.ts
231 changes: 125 additions & 106 deletions integration/crud-typeorm/seeds.ts
Original file line number Diff line number Diff line change
@@ -1,125 +1,144 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
import { plainToClass } from 'class-transformer';
import { MigrationInterface, Repository, QueryRunner } from 'typeorm';
import { Company } from './companies';
import { Project, UserProject } from './projects';
import { Name, User } from './users';
import { License, UserLicense } from './users-licenses';
import { UserProfile } from './users-profiles';

export class Seeds1544303473346 implements MigrationInterface {
private save<T>(repo: Repository<T>, type: any, data: Partial<T>[]): Promise<T[]> {
return repo.save(
data.map((partial: Partial<T>) =>
plainToClass<any, any>(type, partial, { ignoreDecorators: true }),
),
);
}

public async up(queryRunner: QueryRunner): Promise<any> {
const { connection } = queryRunner;

const companiesRepo = connection.getRepository(Company);
const projectsRepo = connection.getRepository(Project);
const usersProfilesRepo = connection.getRepository(UserProfile);
const usersRepo = connection.getRepository(User);
const licensesRepo = connection.getRepository(License);
const usersLincesesRepo = connection.getRepository(UserLicense);
const usersProjectsRepo = connection.getRepository(UserProject);

// companies
await queryRunner.query(`
INSERT INTO public.companies ("name", "domain") VALUES
('Name1', 'Domain1'),
('Name2', 'Domain2'),
('Name3', 'Domain3'),
('Name4', 'Domain4'),
('Name5', 'Domain5'),
('Name6', 'Domain6'),
('Name7', 'Domain7'),
('Name8', 'Domain8'),
('Name9', 'Domain9'),
('Name10', 'Domain10');
`);
await this.save(companiesRepo, Company, [
{ name: 'Name1', domain: 'Domain1' },
{ name: 'Name2', domain: 'Domain2' },
{ name: 'Name3', domain: 'Domain3' },
{ name: 'Name4', domain: 'Domain4' },
{ name: 'Name5', domain: 'Domain5' },
{ name: 'Name6', domain: 'Domain6' },
{ name: 'Name7', domain: 'Domain7' },
{ name: 'Name8', domain: 'Domain8' },
{ name: 'Name9', domain: 'Domain9' },
{ name: 'Name10', domain: 'Domain10' },
]);

// projects
await queryRunner.query(`
INSERT INTO public.projects ("name", "description", "isActive", "companyId") VALUES
('Project1', 'description1', true, 1),
('Project2', 'description2', true, 1),
('Project3', 'description3', true, 2),
('Project4', 'description4', true, 2),
('Project5', 'description5', true, 3),
('Project6', 'description6', true, 3),
('Project7', 'description7', true, 4),
('Project8', 'description8', true, 4),
('Project9', 'description9', true, 5),
('Project10', 'description10', true, 5),
('Project11', 'description11', false, 6),
('Project12', 'description12', false, 6),
('Project13', 'description13', false, 7),
('Project14', 'description14', false, 7),
('Project15', 'description15', false, 8),
('Project16', 'description16', false, 8),
('Project17', 'description17', false, 9),
('Project18', 'description18', false, 9),
('Project19', 'description19', false, 10),
('Project20', 'description20', false, 10);
`);
await this.save(projectsRepo, Project, [
{ name: 'Project1', description: 'description1', isActive: true, companyId: 1 },
{ name: 'Project2', description: 'description2', isActive: true, companyId: 1 },
{ name: 'Project3', description: 'description3', isActive: true, companyId: 2 },
{ name: 'Project4', description: 'description4', isActive: true, companyId: 2 },
{ name: 'Project5', description: 'description5', isActive: true, companyId: 3 },
{ name: 'Project6', description: 'description6', isActive: true, companyId: 3 },
{ name: 'Project7', description: 'description7', isActive: true, companyId: 4 },
{ name: 'Project8', description: 'description8', isActive: true, companyId: 4 },
{ name: 'Project9', description: 'description9', isActive: true, companyId: 5 },
{ name: 'Project10', description: 'description10', isActive: true, companyId: 5 },
{ name: 'Project11', description: 'description11', isActive: false, companyId: 6 },
{ name: 'Project12', description: 'description12', isActive: false, companyId: 6 },
{ name: 'Project13', description: 'description13', isActive: false, companyId: 7 },
{ name: 'Project14', description: 'description14', isActive: false, companyId: 7 },
{ name: 'Project15', description: 'description15', isActive: false, companyId: 8 },
{ name: 'Project16', description: 'description16', isActive: false, companyId: 8 },
{ name: 'Project17', description: 'description17', isActive: false, companyId: 9 },
{ name: 'Project18', description: 'description18', isActive: false, companyId: 9 },
{ name: 'Project19', description: 'description19', isActive: false, companyId: 10 },
{ name: 'Project20', description: 'description20', isActive: false, companyId: 10 },
]);

// user-profiles
await queryRunner.query(`
INSERT INTO public.user_profiles ("name") VALUES
('User1'),
('User2'),
('User3'),
('User4'),
('User5'),
('User6'),
('User7'),
('User8'),
('User9'),
('User1'),
('User1'),
('User1'),
('User1'),
('User1'),
('User1'),
('User1'),
('User1'),
('User1'),
('User1'),
('User2');
`);
await this.save(usersProfilesRepo, UserProfile, [
{ name: 'User1' },
{ name: 'User2' },
{ name: 'User3' },
{ name: 'User4' },
{ name: 'User5' },
{ name: 'User6' },
{ name: 'User7' },
{ name: 'User8' },
{ name: 'User9' },
{ name: 'User1' },
{ name: 'User1' },
{ name: 'User1' },
{ name: 'User1' },
{ name: 'User1' },
{ name: 'User1' },
{ name: 'User1' },
{ name: 'User1' },
{ name: 'User1' },
{ name: 'User1' },
{ name: 'User2' },
]);

// users
await queryRunner.query(`
INSERT INTO public.users ("email", "isActive", "companyId", "profileId", "nameFirst", "nameLast") VALUES
('[email protected]', true, 1, 1, 'firstname1', 'lastname1'),
('[email protected]', true, 1, 2, NULL, NULL),
('[email protected]', true, 1, 3, NULL, NULL),
('[email protected]', true, 1, 4, NULL, NULL),
('[email protected]', true, 1, 5, NULL, NULL),
('[email protected]', true, 1, 6, NULL, NULL),
('[email protected]', false, 1, 7, NULL, NULL),
('[email protected]', false, 1, 8, NULL, NULL),
('[email protected]', false, 1, 9, NULL, NULL),
('[email protected]', true, 1, 10, NULL, NULL),
('[email protected]', true, 2, 11, NULL, NULL),
('[email protected]', true, 2, 12, NULL, NULL),
('[email protected]', true, 2, 13, NULL, NULL),
('[email protected]', true, 2, 14, NULL, NULL),
('[email protected]', true, 2, 15, NULL, NULL),
('[email protected]', true, 2, 16, NULL, NULL),
('[email protected]', false, 2, 17, NULL, NULL),
('[email protected]', false, 2, 18, NULL, NULL),
('[email protected]', false, 2, 19, NULL, NULL),
('[email protected]', false, 2, 20, NULL, NULL),
('[email protected]', false, 2, NULL, NULL, NULL);
`);
const name: Name = { first: null, last: null };
const name1: Name = { first: 'firstname1', last: 'lastname1' };
await this.save(usersRepo, User, [
{ email: '[email protected]', isActive: true, companyId: 1, profileId: 1, name: name1 },
{ email: '[email protected]', isActive: true, companyId: 1, profileId: 2, name },
{ email: '[email protected]', isActive: true, companyId: 1, profileId: 3, name },
{ email: '[email protected]', isActive: true, companyId: 1, profileId: 4, name },
{ email: '[email protected]', isActive: true, companyId: 1, profileId: 5, name },
{ email: '[email protected]', isActive: true, companyId: 1, profileId: 6, name },
{ email: '[email protected]', isActive: false, companyId: 1, profileId: 7, name },
{ email: '[email protected]', isActive: false, companyId: 1, profileId: 8, name },
{ email: '[email protected]', isActive: false, companyId: 1, profileId: 9, name },
{ email: '[email protected]', isActive: true, companyId: 1, profileId: 10, name },
{ email: '[email protected]', isActive: true, companyId: 2, profileId: 11, name },
{ email: '[email protected]', isActive: true, companyId: 2, profileId: 12, name },
{ email: '[email protected]', isActive: true, companyId: 2, profileId: 13, name },
{ email: '[email protected]', isActive: true, companyId: 2, profileId: 14, name },
{ email: '[email protected]', isActive: true, companyId: 2, profileId: 15, name },
{ email: '[email protected]', isActive: true, companyId: 2, profileId: 16, name },
{ email: '[email protected]', isActive: false, companyId: 2, profileId: 17, name },
{ email: '[email protected]', isActive: false, companyId: 2, profileId: 18, name },
{ email: '[email protected]', isActive: false, companyId: 2, profileId: 19, name },
{ email: '[email protected]', isActive: false, companyId: 2, profileId: 20, name },
{ email: '[email protected]', isActive: false, companyId: 2, profileId: null, name },
]);

// licenses
await queryRunner.query(`
INSERT INTO public.licenses ("name") VALUES
('License1'),
('License2'),
('License3'),
('License4'),
('License5');
`);
await this.save(licensesRepo, License, [
{ name: 'License1' },
{ name: 'License2' },
{ name: 'License3' },
{ name: 'License4' },
{ name: 'License5' },
]);

// user-licenses
await queryRunner.query(`
INSERT INTO public.user_licenses ("userId", "licenseId", "yearsActive") VALUES
(1, 1, 3),
(1, 2, 5),
(1, 4, 7),
(2, 5, 1);
`);
await this.save(usersLincesesRepo, UserLicense, [
{ userId: 1, licenseId: 1, yearsActive: 3 },
{ userId: 1, licenseId: 2, yearsActive: 5 },
{ userId: 1, licenseId: 4, yearsActive: 7 },
{ userId: 2, licenseId: 5, yearsActive: 1 },
]);

// user-projects
await queryRunner.query(`
INSERT INTO public.user_projects ("projectId", "userId", "review") VALUES
(1, 1, 'User project 1 1'),
(1, 2, 'User project 1 2'),
(2, 2, 'User project 2 2'),
(3, 3, 'User project 3 3');
`);
await this.save(usersProjectsRepo, UserProject, [
{ projectId: 1, userId: 1, review: 'User project 1 1' },
{ projectId: 1, userId: 2, review: 'User project 1 2' },
{ projectId: 2, userId: 2, review: 'User project 2 2' },
{ projectId: 3, userId: 3, review: 'User project 3 3' },
]);
}

public async down(queryRunner: QueryRunner): Promise<any> {}
Expand Down
4 changes: 4 additions & 0 deletions integration/shared/https-exception.filter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ export class HttpExceptionFilter implements ExceptionFilter {
}

prepareException(exc: any): { status: number; json: object } {
if (process.env.NODE_ENV !== 'test') {
console.log(exc);
}

const error =
exc instanceof HttpException ? exc : new InternalServerErrorException(exc.message);
const status = error.getStatus();
Expand Down
21 changes: 13 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,19 @@
"rebuild": "yarn clean && yarn build",
"build": "yarn s build",
"clean": "yarn s clean",
"pretest": "npm run db:prepare:typeorm",
"test": "yarn s test",
"pretest:coveralls": "yarn pretest",
"test:coveralls": "yarn s test.coveralls",
"test": "npx jest --runInBand -c=jest.config.js packages/ --verbose",
"test:coverage": "yarn test:all --coverage",
"test:coveralls": "yarn test:coverage --coverageReporters=text-lcov | coveralls",
"test:all": "yarn test:mysql && yarn test:postgres",
"test:postgres": "yarn db:prepare:typeorm && yarn test",
"test:mysql": "yarn db:prepare:typeorm:mysql && TYPEORM_CONNECTION=mysql yarn test",
"start:typeorm": "npx nodemon -w ./integration/crud-typeorm -e ts node_modules/ts-node/dist/bin.js integration/crud-typeorm/main.ts",
"db:sync:typeorm": "cd ./integration/crud-typeorm && npx ts-node -r tsconfig-paths/register ../../node_modules/typeorm/cli.js schema:sync -f=orm",
"db:drop:typeorm": "cd ./integration/crud-typeorm && npx ts-node -r tsconfig-paths/register ../../node_modules/typeorm/cli.js schema:drop -f=orm",
"db:seeds:typeorm": "cd ./integration/crud-typeorm && npx ts-node -r tsconfig-paths/register ../../node_modules/typeorm/cli.js migration:run -f=orm",
"db:prepare:typeorm": "npm run db:drop:typeorm && npm run db:sync:typeorm && npm run db:seeds:typeorm",
"db:cli:typeorm": "cd ./integration/crud-typeorm && npx ts-node -r tsconfig-paths/register ../../node_modules/typeorm/cli.js",
"db:sync:typeorm": "yarn db:cli:typeorm schema:sync -f=orm",
"db:drop:typeorm": "yarn db:cli:typeorm schema:drop -f=orm",
"db:seeds:typeorm": "yarn db:cli:typeorm migration:run -f=orm",
"db:prepare:typeorm": "yarn db:drop:typeorm && yarn db:sync:typeorm && yarn db:seeds:typeorm",
"db:prepare:typeorm:mysql": "yarn db:drop:typeorm -c=mysql && yarn db:sync:typeorm -c=mysql && yarn db:seeds:typeorm -c=mysql",
"format": "npx pretty-quick --pattern \"packages/**/!(*.d).ts\"",
"lint": "npx tslint 'packages/**/*.ts'",
"cm": "npx git-cz",
Expand Down Expand Up @@ -74,6 +78,7 @@
"jest": "24.9.0",
"jest-extended": "0.11.2",
"lerna": "3.16.4",
"mysql": "^2.18.1",
"nodemon": "1.19.2",
"npm-check": "5.9.0",
"nps": "5.9.8",
Expand Down
Loading

0 comments on commit 23f5029

Please sign in to comment.