HM-98. Описаны все ошибки api для swagger'a

This commit is contained in:
vigdorov
2020-08-01 00:46:28 +03:00
parent 3711ef5f8f
commit b4e2ded4df
6 changed files with 237 additions and 66 deletions

View File

@ -1,10 +1,34 @@
import {Controller, Get, Req, Post, Options, Header, Delete, HttpCode, Put} from '@nestjs/common';
import {ApiResponse, ApiTags, ApiParam, ApiBody} from '@nestjs/swagger';
import {ALLOW_ORIGIN_ALL, ALLOW_METHOD, ALLOW_CREDENTIALS, CONTENT_LENGTH, ALLOW_HEADERS, USERS_CONTROLLER} from '../consts';
import {UserService} from './users.service';
import {UserResponse, CreateUserRequest, UpdateUserRequest} from './users.schema';
import {Request} from 'express';
import { ApiResponse, ApiTags, ApiParam, ApiBody } from '@nestjs/swagger';
import {
ALLOW_ORIGIN_ALL,
ALLOW_METHOD,
ALLOW_CREDENTIALS,
CONTENT_LENGTH,
ALLOW_HEADERS,
USERS_CONTROLLER,
} from '../consts';
import { UserService } from './users.service';
import {
UserResponse,
CreateUserRequest,
UpdateUserRequest,
} from './users.schema';
import { Request } from 'express';
import {
FIND_ALL_SUCCESS,
FIND_ONE_SUCCESS,
FIND_ONE_NOT_FOUND,
CREATE_SUCCESS,
CREATE_CONFLICT,
CREATE_NOT_VALID,
UPDATE_SUCCESS,
UPDATE_NOT_FOUND,
UPDATE_NOT_VALID,
REMOVE_SUCCESS,
REMOVE_NOT_FOUND,
} from './users.responses';
@Controller(USERS_CONTROLLER)
@ApiTags(USERS_CONTROLLER)
@ -15,21 +39,15 @@ export class UsersController {
@Get()
@Header(...ALLOW_ORIGIN_ALL)
@ApiResponse({
status: 200,
description: 'Список всех пользователей',
type: [UserResponse]
})
@ApiResponse(FIND_ALL_SUCCESS)
async findAll(): Promise<UserResponse[]> {
return this.userService.findAll();
}
@Get(':login')
@Header(...ALLOW_ORIGIN_ALL)
@ApiResponse({
status: 200,
description: 'Получить пользователя по логину'
})
@ApiResponse(FIND_ONE_SUCCESS)
@ApiResponse(FIND_ONE_NOT_FOUND)
@ApiParam({
name: 'login',
description: 'Логин пользователя',
@ -40,14 +58,12 @@ export class UsersController {
@Post()
@Header(...ALLOW_ORIGIN_ALL)
@ApiResponse({
status: 201,
description: 'Создает пользователя в системе',
type: UserResponse,
})
@ApiResponse(CREATE_SUCCESS)
@ApiResponse(CREATE_CONFLICT)
@ApiResponse(CREATE_NOT_VALID)
@ApiBody({
type: CreateUserRequest,
description: 'Принимает объект для создания пользователя'
description: 'Объект для создания пользователя'
})
async createUser(@Req() request: Request<null, CreateUserRequest>): Promise<UserResponse> {
return await this.userService.create(request.body);
@ -55,14 +71,12 @@ export class UsersController {
@Put()
@Header(...ALLOW_ORIGIN_ALL)
@ApiResponse({
status: 200,
description: 'Обновляет данные пользователя',
type: UpdateUserRequest
})
@ApiResponse(UPDATE_SUCCESS)
@ApiResponse(UPDATE_NOT_FOUND)
@ApiResponse(UPDATE_NOT_VALID)
@ApiBody({
type: UpdateUserRequest,
description: 'Принимает объект для обновления данных пользователя'
description: 'Объект обновления данных пользователя'
})
async updateUser(@Req() request: Request<null, UpdateUserRequest>): Promise<UserResponse> {
return await this.userService.update(request.body);
@ -70,11 +84,8 @@ export class UsersController {
@Delete(':login')
@Header(...ALLOW_ORIGIN_ALL)
@ApiResponse({
status: 200,
description: 'Удаляет пользователя',
type: UpdateUserRequest,
})
@ApiResponse(REMOVE_SUCCESS)
@ApiResponse(REMOVE_NOT_FOUND)
@ApiParam({
name: 'login',
description: 'Логин пользователя',

View File

@ -0,0 +1,80 @@
import {ApiResponseOptions, ApiProperty} from '@nestjs/swagger';
import {UserResponse, UpdateUserRequest} from './users.schema';
class Error {
@ApiProperty()
statusCode: number;
@ApiProperty()
message: string;
@ApiProperty()
error: string;
}
export const FIND_ALL_SUCCESS: ApiResponseOptions = {
status: 200,
description: 'Список всех пользователей',
type: UserResponse,
isArray: true
};
export const FIND_ONE_SUCCESS: ApiResponseOptions = {
status: 200,
description: 'Пользователь найденный по логину',
type: UserResponse,
};
export const FIND_ONE_NOT_FOUND: ApiResponseOptions = {
status: 404,
description: 'Ошибка при попытке получить несуществующего пользователя',
type: Error,
};
export const CREATE_SUCCESS: ApiResponseOptions = {
status: 201,
description: 'Создает пользователя в системе',
type: UserResponse,
};
export const CREATE_CONFLICT: ApiResponseOptions = {
status: 409,
description: 'Объект вновь созданного пользователя',
type: Error,
};
export const CREATE_NOT_VALID: ApiResponseOptions = {
status: 400,
description: 'Ошибка при попытке создания пользователя с уже существующим логином',
type: Error,
};
export const UPDATE_SUCCESS: ApiResponseOptions = {
status: 200,
description: 'Объект для обновления пользователя',
type: UpdateUserRequest,
};
export const UPDATE_NOT_FOUND: ApiResponseOptions = {
status: 404,
description: 'Ошибка при попытке обновить пользователя с несуществующим логином',
type: Error,
};
export const UPDATE_NOT_VALID: ApiResponseOptions = {
status: 400,
description: 'Ошибка при попытке обновить пользователя с невалидными полями',
type: Error,
};
export const REMOVE_SUCCESS: ApiResponseOptions = {
status: 200,
description: 'Объект удаленного пользователя',
type: UpdateUserRequest,
};
export const REMOVE_NOT_FOUND: ApiResponseOptions = {
status: 404,
description: 'Ошибка при попытке удалить пользователя с несуществующим логином',
type: Error,
};

View File

@ -1,5 +1,5 @@
import {Model, Connection, Document} from 'mongoose';
import {Injectable, NotFoundException, BadGatewayException, UnauthorizedException} from '@nestjs/common';
import {Injectable, NotFoundException, BadGatewayException, ConflictException, BadRequestException} from '@nestjs/common';
import {InjectConnection} from '@nestjs/mongoose';
import {DB_NAME, USERS_CONTROLLER, SECRET_JWT_ACCESS_KEY, SECRET_JWT_REFRESH_KEY} from 'src/consts';
import {User, UserSchema, CreateUserRequest, UserResponse, UserModel, UpdateUserRequest, TokenResponse} from './users.schema';
@ -53,7 +53,7 @@ export class UserService {
async findOne(login: string): Promise<UserResponse> {
const user = await this.userModel().findOne({login});
if (!user) {
throw new NotFoundException(`Пользователь с логином ${login} не найден`);
throw new NotFoundException(`Not found user "${login}"`);
}
return prepareUserToUserResponse(user);
}
@ -67,19 +67,29 @@ export class UserService {
const searchUser = await this.findUser(user.login);
if (searchUser) {
throw new NotFoundException(`Пользователь с логином ${user.login} уже существует`);
throw new ConflictException(`User login "${user.login}" is already in use`);
}
const Model = await this.userModel();
try {
const checkUser = new Model(user);
await validateModel(checkUser);
} catch (e) {
if (e?.message?.includes('validation failed')) {
throw new BadRequestException(e.message);
}
throw e;
}
const salt = await bcrypt.genSalt(10);
const password = await bcrypt.hash(user.password, salt);
const Model = await this.userModel();
const createUser = new Model({
...user,
salt,
password,
});
await validateModel(createUser);
const savedUser = await createUser.save();
@ -89,12 +99,24 @@ export class UserService {
async update(user: UpdateUserRequest): Promise<UserResponse> {
const searchUser = await this.userModel().findOne({login: user.login});
if (!searchUser) {
throw new NotFoundException(`Not found user login "${user.login}"`)
}
const Model = await this.userModel();
const updateUser = new Model({
...user,
password: searchUser.password,
});
await validateModel(updateUser);
try {
await validateModel(updateUser);
} catch (e) {
if (e?.message?.includes('validation failed')) {
throw new BadRequestException(e.message);
}
throw e;
}
await searchUser.updateOne({
...{
@ -108,6 +130,10 @@ export class UserService {
async removeOne(login: string): Promise<UserResponse> {
const searchUser = await this.userModel().findOne({login});
if (!searchUser) {
throw new NotFoundException(`Not found user login "${login}"`);
}
await this.userModel().deleteOne({login});
return prepareUserToUserResponse(searchUser);
@ -144,14 +170,14 @@ export class UserService {
return this.generateTokens(login, host, agent);
}
throw new UnauthorizedException('Не верный пользователь или пароль');
throw new BadRequestException('Invalid user or password');
}
verifyToken(token: string, secret: string): void {
try {
jwt.verify(token, secret);
} catch (e) {
throw new UnauthorizedException('Авторизация устарела');
throw new BadRequestException('Authorization is outdated');
}
}
@ -166,11 +192,16 @@ export class UserService {
if (searchUser && this.checkToken(token, host, agent)) {
return this.generateTokens(token.login, host, agent);
}
throw new UnauthorizedException('Не санкционированный запрос');
throw new BadRequestException('Unauthorized request');
}
async checkAccessToken(access_token: string, host: string, agent: string): Promise<boolean> {
this.verifyToken(access_token, SECRET_JWT_ACCESS_KEY);
try {
this.verifyToken(access_token, SECRET_JWT_ACCESS_KEY);
} catch (e) {
return false;
}
const token = jwt.decode(access_token) as Token;
const searchUser = await this.findUser(token.login);
return searchUser && this.checkToken(token, host, agent);