Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: userController [ISSUE-1,9,65] #66

Merged
merged 4 commits into from
Jun 24, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions server/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
NODE_ENV=dev
PORT=3000
12 changes: 12 additions & 0 deletions server/docker-compose.test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
version: '3.3'

services:
db:
image: postgres
restart: always
environment:
POSTGRES_USER: test
POSTGRES_DB: hatchout
POSTGRES_PASSWORD: test
ports:
- 5432:5432
24 changes: 24 additions & 0 deletions server/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
version: '3.3'

services:
db:
image: mysql
command: --default-authentication-plugin=mysql_native_password
restart: always
environment:
MYSQL_ROOT_PASSWORD: de-labtory
MYSQL_USER: de-labtory
MYSQL_PASSWORD: de-labtory
MYSQL_DATABASE: hatchout
volumes:
- data:/var/lib/mysql
container_name: typeorm-mysql
ports:
- 3306:3306
adminer:
image: adminer
restart: always
ports:
- 8080:8080
volumes:
data:
2 changes: 2 additions & 0 deletions server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"class-validator": "^0.9.1",
"nestjs-config": "^1.4.0",
"nestjs-typeorm-paginate": "^0.1.7",
"pg": "^7.11.0",
"reflect-metadata": "^0.1.12",
"rimraf": "^2.6.2",
"rxjs": "^6.3.3",
Expand All @@ -39,6 +40,7 @@
"@types/jest": "^23.3.13",
"@types/node": "^10.12.18",
"@types/supertest": "^2.0.7",
"assert": "^2.0.0",
"husky": "^2.3.0",
"jest": "^23.6.0",
"jest-plugin-context": "^2.9.0",
Expand Down
5 changes: 3 additions & 2 deletions server/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,19 @@ import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ConfigModule } from 'nestjs-config';
import { GhostService } from './app/ghost/ghost.service';
import { GhostModule } from './port/module/ghost.module';
import * as path from 'path';
import {UserModule} from './port/module/user.module';
import {DatabaseModule} from './port/module/database.module';

@Module({
imports: [
ConfigModule.load(path.resolve(__dirname, 'config', '**/!(*.d).{ts,js}')),
GhostModule,
UserModule,
DatabaseModule,
],
controllers: [AppController],
providers: [AppService, GhostService],
providers: [AppService],
})
export class AppModule {}
88 changes: 62 additions & 26 deletions server/src/app/user/user.service.impl.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ import {UserServiceImpl} from './user.service.impl';
import {anything, instance, mock, when} from 'ts-mockito';
import {TestingModule, Test} from '@nestjs/testing';
import {User} from '../../domain/user/user.entity';
import {UserDto} from '../../domain/user/dto/user.dto';
import {UserRepository} from '../../port/persistence/repository/user.repository.impl';
import {NotAcceptableException, NotFoundException} from '@nestjs/common';
import {UserDto} from './dto/user.dto';
import {ValidationException} from '../../domain/exception/ValidationException';
import {InvalidParameterException} from '../../domain/exception/InvalidParameterException';

describe('UserServiceImpl', () => {
const address = 'testAddress';
Expand All @@ -22,7 +25,7 @@ describe('UserServiceImpl', () => {
providers: [
UserServiceImpl,
{
provide: 'IUserRepository',
provide: 'UserRepository',
useValue: instance(mockRepository),
},
],
Expand All @@ -44,11 +47,13 @@ describe('UserServiceImpl', () => {

expect(await service.get(id)).toBe(user);
});
it('should return undefined', async () => {
it('should throw NotFoundException', async () => {
when(mockRepository.findById(null)).thenReturn(undefined);
service = new UserServiceImpl(instance(mockRepository));

expect(await service.get(null)).toBeUndefined();
await expect(service.get(null))
.rejects
.toThrowError(NotFoundException);
});
});
describe('#create()', () => {
Expand All @@ -64,7 +69,7 @@ describe('UserServiceImpl', () => {

expect(await service.create(userDto)).toBe(user);
});
it('should throw "address should be defined"', async () => {
it('should throw InvalidParameterException', async () => {
when(mockRepository.findByAddress(address)).thenReturn(
new Promise((resolve => {resolve(user); }),
),
Expand All @@ -74,9 +79,9 @@ describe('UserServiceImpl', () => {

await expect(service.create(userDto))
.rejects
.toThrowError('address should be defined');
.toThrowError(InvalidParameterException);
});
it('should throw "name should be defined"', async () => {
it('should throw InvalidParameterException', async () => {
when(mockRepository.findByAddress(address)).thenReturn(
new Promise((resolve => {resolve(user); }),
),
Expand All @@ -86,9 +91,9 @@ describe('UserServiceImpl', () => {

await expect(service.create(userDto))
.rejects
.toThrowError('name should be defined');
.toThrowError(InvalidParameterException);
});
it('should throw "address is already registered"', async () => {
it('should throw NotAcceptableException', async () => {
when(mockRepository.findByAddress(address)).thenReturn(
new Promise((resolve => {resolve(user); }),
),
Expand All @@ -98,7 +103,7 @@ describe('UserServiceImpl', () => {

await expect(service.create(userDto))
.rejects
.toThrowError('address is already registered');
.toThrowError(NotAcceptableException);
});
});
describe('#delete()', () => {
Expand All @@ -120,31 +125,41 @@ describe('UserServiceImpl', () => {
});
describe('#increasePoint()', () => {
const id = 1;
const amount = 10;
const validAmount = 10;
const invalidAmount = -1;

it('should return user with increased point', async () => {
when(mockRepository.findById(id)).thenReturn(new Promise((resolve => resolve(user))));
service = new UserServiceImpl(instance(mockRepository));

const userReturned = await service.increasePoint(id, amount);
const userReturned = await service.increasePoint(id, validAmount);

expect(userReturned).toBeDefined();
expect(userReturned.getPoint()).toBe(amount);
expect(userReturned.getPoint()).toBe(validAmount);
});
it('should throw error "user with the id is not found"', async () => {
it('should throw error NotFoundException', async () => {
when(mockRepository.findById(id)).thenReturn(undefined);
service = new UserServiceImpl(instance(mockRepository));

await expect(service.increasePoint(id, amount))
await expect(service.increasePoint(id, validAmount))
.rejects
.toThrowError('user with the id is not found');
.toThrowError(NotFoundException);
});
it('should throw ValidationException when amount is negative', async () => {
when(mockRepository.findById(id)).thenReturn(new Promise((resolve => resolve(user))));
service = new UserServiceImpl(instance(mockRepository));

await expect(service.increasePoint(id, invalidAmount))
.rejects
.toThrowError(ValidationException);

});
});
describe('#decreasePoint()', () => {
const id = 1;
const point = 100;
const validAmount = 10;
const invalidAmount = 1000;
let invalidAmount: number;

it('should return user with increased level', async () => {
user = new User(address, name, point);
Expand All @@ -156,28 +171,39 @@ describe('UserServiceImpl', () => {
expect(userReturned).toBeDefined();
expect(userReturned.getPoint()).toBe(point - validAmount);
});
it('should throw error "user with the id is not found"', async () => {
it('should throw error NotFoundException', async () => {
when(mockRepository.findById(id)).thenReturn(undefined);
service = new UserServiceImpl(instance(mockRepository));

await expect(service.decreasePoint(id, validAmount))
.rejects
.toThrowError('user with the id is not found');
.toThrowError(NotFoundException);
});
it('should throw error "can not decrease point"', async () => {
it('should throw ValidationException when amount is negative', async () => {
invalidAmount = -1;
user = new User(address, name, point);
when(mockRepository.findById(id)).thenReturn(new Promise((resolve => resolve(user))));
service = new UserServiceImpl(instance(mockRepository));

await expect(service.decreasePoint(id, invalidAmount))
.rejects
.toThrowError('can not decrease point');
.toThrowError(ValidationException);
});
it('should throw ValidationException when point becomes less than MIN_POINT', async () => {
invalidAmount = 1000;
user = new User(address, name, point);
when(mockRepository.findById(id)).thenReturn(new Promise((resolve => resolve(user))));
service = new UserServiceImpl(instance(mockRepository));

await expect(service.decreasePoint(id, invalidAmount))
.rejects
.toThrowError(ValidationException);
});
});
describe('#increaseLevel()', () => {
const id = 1;
const validAmount = 10;
const invalidAmount = 1000;
let invalidAmount: number;

it('should return user with increased level', async () => {
when(mockRepository.findById(id)).thenReturn(new Promise((resolve => resolve(user))));
Expand All @@ -188,21 +214,31 @@ describe('UserServiceImpl', () => {
expect(userReturned).toBeDefined();
expect(userReturned.getLevel()).toBe(validAmount);
});
it('should throw error "user with the id is not found"', async () => {
it('should throw error NotFoundException', async () => {
when(mockRepository.findById(id)).thenReturn(undefined);
service = new UserServiceImpl(instance(mockRepository));

await expect(service.increaseLevel(id, validAmount))
.rejects
.toThrowError('user with the id is not found');
.toThrowError(NotFoundException);
});
it('should throw ValidationException when amount is negative', async () => {
invalidAmount = -1;
when(mockRepository.findById(id)).thenReturn(new Promise((resolve => resolve(user))));
service = new UserServiceImpl(instance(mockRepository));

await expect(service.increaseLevel(id, invalidAmount))
.rejects
.toThrowError(ValidationException);
});
it('should throw error "can not increase level"', async () => {
it('should throw ValidationException when level becomes more than MAX_LEVEL', async () => {
invalidAmount = 1000;
when(mockRepository.findById(id)).thenReturn(new Promise((resolve => resolve(user))));
service = new UserServiceImpl(instance(mockRepository));

await expect(service.increaseLevel(id, invalidAmount))
.rejects
.toThrowError('can not increase level');
.toThrowError(ValidationException);
});
});
});
47 changes: 19 additions & 28 deletions server/src/app/user/user.service.impl.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,34 @@
import {Inject, Injectable} from '@nestjs/common';
import {BadRequestException, Injectable, NotAcceptableException, NotFoundException} from '@nestjs/common';
import {UserService} from './user.service';
import {UserDto} from '../../domain/user/dto/user.dto';
import {User} from '../../domain/user/user.entity';
import {DeleteResult} from 'typeorm';
import {IUserRepository} from '../../domain/user/user.repository';
import {InjectRepository} from '@nestjs/typeorm';
import {UserDto} from './dto/user.dto';
import {InvalidParameterException} from '../../domain/exception/InvalidParameterException';

@Injectable()
export class UserServiceImpl implements UserService {
constructor(@Inject('IUserRepository') private userRepository: IUserRepository) {}
constructor(@InjectRepository(User) private userRepository: IUserRepository) {}

async get(id: number): Promise<User> {
return await this.userRepository.findById(id);
const user = await this.userRepository.findById(id);
if (user === undefined) {
throw new NotFoundException('user with the id is not found');
}
return user;
}

async create(userDto: UserDto): Promise<User> {
if (userDto.address === undefined) {
throw new Error('address should be defined');
throw new InvalidParameterException('address should be defined');
}
if (userDto.name === undefined) {
throw new Error('name should be defined');
throw new InvalidParameterException('name should be defined');
}
const userRetrieved = await this.userRepository.findByAddress(userDto.address);
if (userRetrieved !== undefined) {
throw new Error('address is already registered');
throw new NotAcceptableException('address is already registered');
}

const user = new User(userDto.address, userDto.name);
Expand All @@ -34,41 +40,26 @@ export class UserServiceImpl implements UserService {
}

async increasePoint(id: number, amount: number): Promise<User> {
let user: User;
user = await this.userRepository.findById(id);
const user = await this.userRepository.findById(id);
if (user === undefined) {
throw new Error('user with the id is not found');
throw new NotFoundException('user with the id is not found');
}

return user.increasePoint(amount);
}

async decreasePoint(id: number, amount: number): Promise<User> {
let user: User;
user = await this.userRepository.findById(id);
const user = await this.userRepository.findById(id);
if (user === undefined) {
throw new Error('user with the id is not found');
}

let errors: Error[];
errors = user.canDecreasePoint(amount);
if (errors.length !== 0) {
throw new Error('can not decrease point');
throw new NotFoundException('user with the id is not found');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In line 17, you throw BadRequestException, but in line 54, you throw NotFoundException.

Why did you separated "finding undefined user" to BadRequestException and NotFoundException?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks!! very nice

}

return await user.decreasePoint(amount);
}
async increaseLevel(id: number, amount: number): Promise<User> {
let user: User;
user = await this.userRepository.findById(id);
const user = await this.userRepository.findById(id);
if (user === undefined) {
throw new Error('user with the id is not found');
}

let errors: Error[];
errors = user.canIncreaseLevel(amount);
if ( errors.length !== 0) {
throw new Error('can not increase level');
throw new NotFoundException('user with the id is not found');
}

return await user.increaseLevel(amount);
Expand Down
2 changes: 1 addition & 1 deletion server/src/app/user/user.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {User} from '../../domain/user/user.entity';
import {UserDto} from '../../domain/user/dto/user.dto';
import {DeleteResult} from 'typeorm';
import {UserDto} from './dto/user.dto';

export interface UserService {
get(id: number): Promise<User>;
Expand Down
12 changes: 12 additions & 0 deletions server/src/config/database/dev.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export default {
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'de-labtory',
password: 'de-labtory',
database: 'hatchout',
logging: false,
synchronize: true,
entities: ['src/domain/**/*.entity{.ts,.js}'],
dropSchema: false,
};
Loading