HM-35. Описаны все ответы ошибок сервера, поправлены ответы и тексты описания. HM-60. Исправлена ошибка падения сервера при попытке найти не существующее хранилище

This commit is contained in:
vigdorov
2020-07-31 22:01:42 +03:00
parent 0c9ae5d2cf
commit 0aa73513c1
5 changed files with 161 additions and 61 deletions

View File

@ -1,8 +1,8 @@
import {Controller, Get, Header, Req, NotFoundException, Delete, Options, HttpCode} from '@nestjs/common'; import {Controller, Get, Header, Delete, Options, HttpCode} from '@nestjs/common';
import {ApiTags, ApiResponse, ApiParam} from '@nestjs/swagger'; import {ApiTags, ApiResponse} from '@nestjs/swagger';
import {LogsService} from './logs.service'; import {LogsService} from './logs.service';
import {ALLOW_ORIGIN_ALL, COLLECTION_LOGS, LOG_TYPE, ALLOW_METHOD, ALLOW_CREDENTIALS, CONTENT_LENGTH, ALLOW_HEADERS} from 'src/consts'; import {ALLOW_ORIGIN_ALL, COLLECTION_LOGS, LOG_TYPE, ALLOW_METHOD, ALLOW_CREDENTIALS, CONTENT_LENGTH, ALLOW_HEADERS} from 'src/consts';
import {ClienLogRequest, ClientLog, ServerLog} from './logs.schema'; import {ClienLogResponse, ServerLogResponse} from './logs.schema';
@Controller(COLLECTION_LOGS) @Controller(COLLECTION_LOGS)
@ApiTags(COLLECTION_LOGS) @ApiTags(COLLECTION_LOGS)
@ -15,16 +15,23 @@ export class LogsController {
@Header(...ALLOW_ORIGIN_ALL) @Header(...ALLOW_ORIGIN_ALL)
@ApiResponse({ @ApiResponse({
status: 200, status: 200,
description: 'Возвращает список всех логов', description: 'Возвращает список клиентских запросов',
type: [ClienLogRequest] type: ClienLogResponse,
isArray: true,
}) })
async findAllClientLogs(): Promise<ClientLog[]> { async findAllClientLogs(): Promise<ClienLogResponse[]> {
return await this.logsService.findAllClientLogs(); return await this.logsService.findAllClientLogs();
} }
@Delete('/client') @Delete('/client')
@Header(...ALLOW_ORIGIN_ALL) @Header(...ALLOW_ORIGIN_ALL)
async clearAllClientLogs(): Promise<[]> { @ApiResponse({
status: 200,
description: 'Пустой список клиентских запросов',
type: ClienLogResponse,
isArray: true,
})
async clearAllClientLogs(): Promise<ClienLogResponse[]> {
return await this.logsService.clearLogsByType(LOG_TYPE.CLIENT); return await this.logsService.clearLogsByType(LOG_TYPE.CLIENT);
} }
@ -32,20 +39,27 @@ export class LogsController {
@Header(...ALLOW_ORIGIN_ALL) @Header(...ALLOW_ORIGIN_ALL)
@ApiResponse({ @ApiResponse({
status: 200, status: 200,
description: 'Возвращает список всех логов', description: 'Возвращает список серверных ошибок',
type: [ClienLogRequest] type: ServerLogResponse,
isArray: true,
}) })
async findAllServerLogs(): Promise<ServerLog[]> { async findAllServerLogs(): Promise<ServerLogResponse[]> {
return await this.logsService.findAllServerLogs(); return await this.logsService.findAllServerLogs();
} }
@Delete('/server') @Delete('/server')
@Header(...ALLOW_ORIGIN_ALL) @Header(...ALLOW_ORIGIN_ALL)
async clearAllServerLogs(): Promise<[]> { @ApiResponse({
status: 200,
description: 'Пустой список серверных ошибок',
type: ServerLogResponse,
isArray: true,
})
async clearAllServerLogs(): Promise<ServerLogResponse[]> {
return await this.logsService.clearLogsByType(LOG_TYPE.SERVER); return await this.logsService.clearLogsByType(LOG_TYPE.SERVER);
} }
@Options() @Options(['', '/server', '/client'])
@Header(...ALLOW_ORIGIN_ALL) @Header(...ALLOW_ORIGIN_ALL)
@Header(...ALLOW_METHOD) @Header(...ALLOW_METHOD)
@Header(...ALLOW_CREDENTIALS) @Header(...ALLOW_CREDENTIALS)

View File

@ -2,7 +2,7 @@ import { Document } from 'mongoose';
import {Prop, Schema, SchemaFactory} from '@nestjs/mongoose'; import {Prop, Schema, SchemaFactory} from '@nestjs/mongoose';
import {ApiProperty} from '@nestjs/swagger'; import {ApiProperty} from '@nestjs/swagger';
export class ClienLogRequest { export class ClienLogResponse {
@ApiProperty() @ApiProperty()
type: string; type: string;
@ -19,7 +19,7 @@ export class ClienLogRequest {
endTime: string; endTime: string;
} }
export class ServerLogRequest { export class ServerLogResponse {
@ApiProperty() @ApiProperty()
type: string; type: string;

View File

@ -5,6 +5,19 @@ import {ApiResponse, ApiTags, ApiParam, ApiBody} from '@nestjs/swagger';
import {ALLOW_ORIGIN_ALL, ALLOW_METHOD, ALLOW_CREDENTIALS, CONTENT_LENGTH, ALLOW_HEADERS, COLLECTION_STORE} from 'src/consts'; import {ALLOW_ORIGIN_ALL, ALLOW_METHOD, ALLOW_CREDENTIALS, CONTENT_LENGTH, ALLOW_HEADERS, COLLECTION_STORE} from 'src/consts';
import {Request} from 'express'; import {Request} from 'express';
import {LoggingInterceptor} from 'src/logs/logging.interceptor'; import {LoggingInterceptor} from 'src/logs/logging.interceptor';
import {
FIND_ALL_SUCCESS,
CREATE_SUCCESS,
CREATE_CONFLICT,
CREATE_NOT_VALID,
FIND_ONE_SUCCESS,
FIND_ONE_NOT_FOUND,
UPDATE_SUCCESS,
UPDATE_NOT_FOUND,
UPDATE_NOT_VALID,
REMOVE_SUCCESS,
REMOVE_NOT_FOUND,
} from './store.responses';
const prepareStoreToStoreRequest = ({ const prepareStoreToStoreRequest = ({
key, value, description, service_name, author key, value, description, service_name, author
@ -27,11 +40,7 @@ export class StoreController {
@Get() @Get()
@Header(...ALLOW_ORIGIN_ALL) @Header(...ALLOW_ORIGIN_ALL)
@ApiResponse({ @ApiResponse(FIND_ALL_SUCCESS)
status: 200,
description: 'Список всех пар ключ-значение',
type: [StoreRequest],
})
async findAll(@Req() request: Request): Promise<StoreRequest[]> { async findAll(@Req() request: Request): Promise<StoreRequest[]> {
const api = makeApiHeader(request); const api = makeApiHeader(request);
const storeList = await this.storeService.findAll(api); const storeList = await this.storeService.findAll(api);
@ -40,14 +49,11 @@ export class StoreController {
@Get(':key') @Get(':key')
@Header(...ALLOW_ORIGIN_ALL) @Header(...ALLOW_ORIGIN_ALL)
@ApiResponse({ @ApiResponse(FIND_ONE_SUCCESS)
status: 200, @ApiResponse(FIND_ONE_NOT_FOUND)
description: 'Возвращает пару ключ-значение по ключу',
type: StoreRequest,
})
@ApiParam({ @ApiParam({
name: 'key', name: 'key',
description: 'Уникальный ключ для получения api', description: 'Ключ для поиска хранилища',
}) })
async findOne(@Req() request: Request<{key: string}>): Promise<StoreRequest> { async findOne(@Req() request: Request<{key: string}>): Promise<StoreRequest> {
const {key} = request.params; const {key} = request.params;
@ -58,14 +64,12 @@ export class StoreController {
@Post() @Post()
@Header(...ALLOW_ORIGIN_ALL) @Header(...ALLOW_ORIGIN_ALL)
@ApiResponse({ @ApiResponse(CREATE_SUCCESS)
status: 201, @ApiResponse(CREATE_CONFLICT)
description: 'Создает новую пару ключ-значение', @ApiResponse(CREATE_NOT_VALID)
type: StoreRequest,
})
@ApiBody({ @ApiBody({
type: StoreRequest, type: StoreRequest,
description: 'Принимает объект для создания api' description: 'Объект для создания хранилища'
}) })
async create(@Req() request: Request<null, StoreRequest>): Promise<StoreRequest> { async create(@Req() request: Request<null, StoreRequest>): Promise<StoreRequest> {
const api = makeApiHeader(request); const api = makeApiHeader(request);
@ -75,14 +79,12 @@ export class StoreController {
@Put() @Put()
@Header(...ALLOW_ORIGIN_ALL) @Header(...ALLOW_ORIGIN_ALL)
@ApiResponse({ @ApiResponse(UPDATE_SUCCESS)
status: 200, @ApiResponse(UPDATE_NOT_FOUND)
description: 'Обновляет по ключу объект и мета-поля, кроме author', @ApiResponse(UPDATE_NOT_VALID)
type: StoreRequest,
})
@ApiBody({ @ApiBody({
type: StoreRequest, type: StoreRequest,
description: 'Принимает объект для обновления api' description: 'Объект для обновления хранилища'
}) })
async update(@Req() request: Request<null, StoreRequest>): Promise<StoreRequest> { async update(@Req() request: Request<null, StoreRequest>): Promise<StoreRequest> {
const api = makeApiHeader(request); const api = makeApiHeader(request);
@ -92,14 +94,11 @@ export class StoreController {
@Delete(':key') @Delete(':key')
@Header(...ALLOW_ORIGIN_ALL) @Header(...ALLOW_ORIGIN_ALL)
@ApiResponse({ @ApiResponse(REMOVE_SUCCESS)
status: 200, @ApiResponse(REMOVE_NOT_FOUND)
description: 'Удаляет пару ключ-значение по ключу',
type: StoreRequest,
})
@ApiParam({ @ApiParam({
name: 'key', name: 'key',
description: 'Уникальный ключ для удаления api', description: 'Ключ для удаления хранилища',
}) })
async removeOne(@Req() request: Request<{key: string}>): Promise<StoreRequest> { async removeOne(@Req() request: Request<{key: string}>): Promise<StoreRequest> {
const {key} = request.params; const {key} = request.params;
@ -108,7 +107,7 @@ export class StoreController {
return prepareStoreToStoreRequest(store); return prepareStoreToStoreRequest(store);
} }
@Options('') @Options(['', ':key'])
@Header(...ALLOW_ORIGIN_ALL) @Header(...ALLOW_ORIGIN_ALL)
@Header(...ALLOW_METHOD) @Header(...ALLOW_METHOD)
@Header(...ALLOW_CREDENTIALS) @Header(...ALLOW_CREDENTIALS)
@ -118,15 +117,4 @@ export class StoreController {
async options(): Promise<string> { async options(): Promise<string> {
return ''; return '';
} }
@Options(':key')
@Header(...ALLOW_ORIGIN_ALL)
@Header(...ALLOW_METHOD)
@Header(...ALLOW_CREDENTIALS)
@Header(...CONTENT_LENGTH)
@Header(...ALLOW_HEADERS)
@HttpCode(204)
async optionsByParam(): Promise<string> {
return '';
}
} }

View File

@ -0,0 +1,80 @@
import {StoreRequest} from './store.schema';
import {ApiResponseOptions, ApiProperty} from '@nestjs/swagger';
class Error {
@ApiProperty()
statusCode: number;
@ApiProperty()
message: string;
@ApiProperty()
error: string;
}
export const FIND_ALL_SUCCESS: ApiResponseOptions = {
status: 200,
description: 'Список всех хранилищ',
type: StoreRequest,
isArray: true
};
export const FIND_ONE_SUCCESS: ApiResponseOptions = {
status: 200,
description: 'Объект хранилища найденный по ключу',
type: StoreRequest,
};
export const FIND_ONE_NOT_FOUND: ApiResponseOptions = {
status: 404,
description: 'Ошибка при попытке получить несуществующее хранилище',
type: Error,
};
export const CREATE_SUCCESS: ApiResponseOptions = {
status: 201,
description: 'Объект вновь созданного хранилища',
type: StoreRequest,
};
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: 'Обновляет поля в объекте хранилища, кроме поля author',
type: StoreRequest,
};
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: StoreRequest,
};
export const REMOVE_NOT_FOUND: ApiResponseOptions = {
status: 404,
description: 'Ошибка при попытке удалить несуществующее хранилище',
type: Error,
};

View File

@ -1,5 +1,5 @@
import {Model, Connection} from 'mongoose'; import {Model, Connection} from 'mongoose';
import {Injectable, NotFoundException, BadGatewayException} from '@nestjs/common'; import {Injectable, NotFoundException, BadGatewayException, ConflictException, BadRequestException} from '@nestjs/common';
import {InjectConnection} from '@nestjs/mongoose'; import {InjectConnection} from '@nestjs/mongoose';
import {Store, StoreRequest, StoreSchema} from './store.schema'; import {Store, StoreRequest, StoreSchema} from './store.schema';
import {DB_TEST_NAME, DB_NAME, COLLECTION_STORE} from 'src/consts'; import {DB_TEST_NAME, DB_NAME, COLLECTION_STORE} from 'src/consts';
@ -34,12 +34,20 @@ export class StoreService {
const searchStore = await this.findOne(api, store.key); const searchStore = await this.findOne(api, store.key);
if (searchStore) { if (searchStore) {
throw new NotFoundException(`Api key ${store.key} is already taken`); throw new ConflictException(`Api key "${store.key}" is already taken`);
} }
const createdStore = new (this.storeModel(api))(store); const createdStore = new (this.storeModel(api))(store);
try {
await validateModel(createdStore); await validateModel(createdStore);
} catch (e) {
if (e?.message?.includes('validation failed')) {
throw new BadRequestException(e.message);
}
throw e;
}
const savedStore = await createdStore.save(); const savedStore = await createdStore.save();
if (!Object.keys(savedStore.value).length) { if (!Object.keys(savedStore.value).length) {
@ -57,7 +65,14 @@ export class StoreService {
author: searchStore.author, author: searchStore.author,
}; };
const updateStore = new (this.storeModel(api))(store); const updateStore = new (this.storeModel(api))(store);
try {
await validateModel(updateStore); await validateModel(updateStore);
} catch (e) {
if (e?.message?.includes('validation failed')) {
throw new BadRequestException(e.message);
}
throw e;
}
await searchStore.updateOne({ await searchStore.updateOne({
...store, ...store,
@ -67,11 +82,14 @@ export class StoreService {
return updateStore; return updateStore;
} }
throw new NotFoundException(`Not Found api key - ${omitProps.key}`); throw new NotFoundException(`Not found api key "${omitProps.key}"`);
} }
async findOne(api: string, key: string): Promise<Store> { async findOne(api: string, key: string): Promise<Store> {
const searchStore = await this.storeModel(api).findOne({key}); const searchStore = await this.storeModel(api).findOne({key});
if (!searchStore) {
throw new NotFoundException(`Not found api key "${key}"`);
}
return searchStore; return searchStore;
} }
@ -84,6 +102,6 @@ export class StoreService {
return searchStore; return searchStore;
} }
throw new NotFoundException(`Not Found key - ${key}`); throw new NotFoundException(`Not found api key "${key}"`);
} }
} }