diff --git a/src/logs/logs.controller.ts b/src/logs/logs.controller.ts index 5f8659b..6f659cb 100644 --- a/src/logs/logs.controller.ts +++ b/src/logs/logs.controller.ts @@ -1,8 +1,8 @@ -import {Controller, Get, Header, Req, NotFoundException, Delete, Options, HttpCode} from '@nestjs/common'; -import {ApiTags, ApiResponse, ApiParam} from '@nestjs/swagger'; +import {Controller, Get, Header, Delete, Options, HttpCode} from '@nestjs/common'; +import {ApiTags, ApiResponse} from '@nestjs/swagger'; 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 {ClienLogRequest, ClientLog, ServerLog} from './logs.schema'; +import {ClienLogResponse, ServerLogResponse} from './logs.schema'; @Controller(COLLECTION_LOGS) @ApiTags(COLLECTION_LOGS) @@ -15,16 +15,23 @@ export class LogsController { @Header(...ALLOW_ORIGIN_ALL) @ApiResponse({ status: 200, - description: 'Возвращает список всех логов', - type: [ClienLogRequest] + description: 'Возвращает список клиентских запросов', + type: ClienLogResponse, + isArray: true, }) - async findAllClientLogs(): Promise { + async findAllClientLogs(): Promise { return await this.logsService.findAllClientLogs(); } @Delete('/client') @Header(...ALLOW_ORIGIN_ALL) - async clearAllClientLogs(): Promise<[]> { + @ApiResponse({ + status: 200, + description: 'Пустой список клиентских запросов', + type: ClienLogResponse, + isArray: true, + }) + async clearAllClientLogs(): Promise { return await this.logsService.clearLogsByType(LOG_TYPE.CLIENT); } @@ -32,20 +39,27 @@ export class LogsController { @Header(...ALLOW_ORIGIN_ALL) @ApiResponse({ status: 200, - description: 'Возвращает список всех логов', - type: [ClienLogRequest] + description: 'Возвращает список серверных ошибок', + type: ServerLogResponse, + isArray: true, }) - async findAllServerLogs(): Promise { + async findAllServerLogs(): Promise { return await this.logsService.findAllServerLogs(); } @Delete('/server') @Header(...ALLOW_ORIGIN_ALL) - async clearAllServerLogs(): Promise<[]> { + @ApiResponse({ + status: 200, + description: 'Пустой список серверных ошибок', + type: ServerLogResponse, + isArray: true, + }) + async clearAllServerLogs(): Promise { return await this.logsService.clearLogsByType(LOG_TYPE.SERVER); } - @Options() + @Options(['', '/server', '/client']) @Header(...ALLOW_ORIGIN_ALL) @Header(...ALLOW_METHOD) @Header(...ALLOW_CREDENTIALS) diff --git a/src/logs/logs.schema.ts b/src/logs/logs.schema.ts index eed20fa..d8de227 100644 --- a/src/logs/logs.schema.ts +++ b/src/logs/logs.schema.ts @@ -2,7 +2,7 @@ import { Document } from 'mongoose'; import {Prop, Schema, SchemaFactory} from '@nestjs/mongoose'; import {ApiProperty} from '@nestjs/swagger'; -export class ClienLogRequest { +export class ClienLogResponse { @ApiProperty() type: string; @@ -19,7 +19,7 @@ export class ClienLogRequest { endTime: string; } -export class ServerLogRequest { +export class ServerLogResponse { @ApiProperty() type: string; diff --git a/src/store/store.controller.ts b/src/store/store.controller.ts index 89ad376..6bfdb91 100644 --- a/src/store/store.controller.ts +++ b/src/store/store.controller.ts @@ -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 {Request} from 'express'; 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 = ({ key, value, description, service_name, author @@ -27,11 +40,7 @@ export class StoreController { @Get() @Header(...ALLOW_ORIGIN_ALL) - @ApiResponse({ - status: 200, - description: 'Список всех пар ключ-значение', - type: [StoreRequest], - }) + @ApiResponse(FIND_ALL_SUCCESS) async findAll(@Req() request: Request): Promise { const api = makeApiHeader(request); const storeList = await this.storeService.findAll(api); @@ -40,14 +49,11 @@ export class StoreController { @Get(':key') @Header(...ALLOW_ORIGIN_ALL) - @ApiResponse({ - status: 200, - description: 'Возвращает пару ключ-значение по ключу', - type: StoreRequest, - }) + @ApiResponse(FIND_ONE_SUCCESS) + @ApiResponse(FIND_ONE_NOT_FOUND) @ApiParam({ name: 'key', - description: 'Уникальный ключ для получения api', + description: 'Ключ для поиска хранилища', }) async findOne(@Req() request: Request<{key: string}>): Promise { const {key} = request.params; @@ -58,14 +64,12 @@ export class StoreController { @Post() @Header(...ALLOW_ORIGIN_ALL) - @ApiResponse({ - status: 201, - description: 'Создает новую пару ключ-значение', - type: StoreRequest, - }) + @ApiResponse(CREATE_SUCCESS) + @ApiResponse(CREATE_CONFLICT) + @ApiResponse(CREATE_NOT_VALID) @ApiBody({ type: StoreRequest, - description: 'Принимает объект для создания api' + description: 'Объект для создания хранилища' }) async create(@Req() request: Request): Promise { const api = makeApiHeader(request); @@ -75,14 +79,12 @@ export class StoreController { @Put() @Header(...ALLOW_ORIGIN_ALL) - @ApiResponse({ - status: 200, - description: 'Обновляет по ключу объект и мета-поля, кроме author', - type: StoreRequest, - }) + @ApiResponse(UPDATE_SUCCESS) + @ApiResponse(UPDATE_NOT_FOUND) + @ApiResponse(UPDATE_NOT_VALID) @ApiBody({ type: StoreRequest, - description: 'Принимает объект для обновления api' + description: 'Объект для обновления хранилища' }) async update(@Req() request: Request): Promise { const api = makeApiHeader(request); @@ -92,14 +94,11 @@ export class StoreController { @Delete(':key') @Header(...ALLOW_ORIGIN_ALL) - @ApiResponse({ - status: 200, - description: 'Удаляет пару ключ-значение по ключу', - type: StoreRequest, - }) + @ApiResponse(REMOVE_SUCCESS) + @ApiResponse(REMOVE_NOT_FOUND) @ApiParam({ name: 'key', - description: 'Уникальный ключ для удаления api', + description: 'Ключ для удаления хранилища', }) async removeOne(@Req() request: Request<{key: string}>): Promise { const {key} = request.params; @@ -108,7 +107,7 @@ export class StoreController { return prepareStoreToStoreRequest(store); } - @Options('') + @Options(['', ':key']) @Header(...ALLOW_ORIGIN_ALL) @Header(...ALLOW_METHOD) @Header(...ALLOW_CREDENTIALS) @@ -118,15 +117,4 @@ export class StoreController { async options(): Promise { return ''; } - - @Options(':key') - @Header(...ALLOW_ORIGIN_ALL) - @Header(...ALLOW_METHOD) - @Header(...ALLOW_CREDENTIALS) - @Header(...CONTENT_LENGTH) - @Header(...ALLOW_HEADERS) - @HttpCode(204) - async optionsByParam(): Promise { - return ''; - } } diff --git a/src/store/store.responses.ts b/src/store/store.responses.ts new file mode 100644 index 0000000..c896733 --- /dev/null +++ b/src/store/store.responses.ts @@ -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, +}; diff --git a/src/store/store.service.ts b/src/store/store.service.ts index 64cdcfb..5df3212 100644 --- a/src/store/store.service.ts +++ b/src/store/store.service.ts @@ -1,5 +1,5 @@ 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 {Store, StoreRequest, StoreSchema} from './store.schema'; 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); 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); - await validateModel(createdStore); + try { + await validateModel(createdStore); + } catch (e) { + if (e?.message?.includes('validation failed')) { + throw new BadRequestException(e.message); + } + throw e; + } + const savedStore = await createdStore.save(); if (!Object.keys(savedStore.value).length) { @@ -57,7 +65,14 @@ export class StoreService { author: searchStore.author, }; const updateStore = new (this.storeModel(api))(store); - await validateModel(updateStore); + try { + await validateModel(updateStore); + } catch (e) { + if (e?.message?.includes('validation failed')) { + throw new BadRequestException(e.message); + } + throw e; + } await searchStore.updateOne({ ...store, @@ -67,11 +82,14 @@ export class StoreService { 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 { const searchStore = await this.storeModel(api).findOne({key}); + if (!searchStore) { + throw new NotFoundException(`Not found api key "${key}"`); + } return searchStore; } @@ -84,6 +102,6 @@ export class StoreService { return searchStore; } - throw new NotFoundException(`Not Found key - ${key}`); + throw new NotFoundException(`Not found api key "${key}"`); } } \ No newline at end of file