HM-35. Описаны все ответы ошибок сервера, поправлены ответы и тексты описания. HM-60. Исправлена ошибка падения сервера при попытке найти не существующее хранилище
This commit is contained in:
@ -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)
|
||||||
|
|||||||
@ -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;
|
||||||
|
|
||||||
|
|||||||
@ -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 '';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
80
src/store/store.responses.ts
Normal file
80
src/store/store.responses.ts
Normal 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,
|
||||||
|
};
|
||||||
@ -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}"`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user