Разделение методов создания и обновления элементов, добавление полей author, description, service_name. Исправление бага при котором не удалялось апи. Удаление из ответа лишних полей __v и _id
This commit is contained in:
24
.eslintrc.js
24
.eslintrc.js
@ -1,24 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
parser: '@typescript-eslint/parser',
|
|
||||||
parserOptions: {
|
|
||||||
project: 'tsconfig.json',
|
|
||||||
sourceType: 'module',
|
|
||||||
},
|
|
||||||
plugins: ['@typescript-eslint/eslint-plugin'],
|
|
||||||
extends: [
|
|
||||||
'plugin:@typescript-eslint/eslint-recommended',
|
|
||||||
'plugin:@typescript-eslint/recommended',
|
|
||||||
'prettier',
|
|
||||||
'prettier/@typescript-eslint',
|
|
||||||
],
|
|
||||||
root: true,
|
|
||||||
env: {
|
|
||||||
node: true,
|
|
||||||
jest: true,
|
|
||||||
},
|
|
||||||
rules: {
|
|
||||||
'@typescript-eslint/interface-name-prefix': 'off',
|
|
||||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
|
||||||
'@typescript-eslint/no-explicit-any': 'off',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
25
.eslintrc.json
Normal file
25
.eslintrc.json
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"parserOptions": {
|
||||||
|
"project": "tsconfig.json",
|
||||||
|
"sourceType": "module"
|
||||||
|
},
|
||||||
|
"plugins": ["@typescript-eslint/eslint-plugin"],
|
||||||
|
"extends": [
|
||||||
|
"plugin:@typescript-eslint/eslint-recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended",
|
||||||
|
"prettier",
|
||||||
|
"prettier/@typescript-eslint"
|
||||||
|
],
|
||||||
|
"root": true,
|
||||||
|
"env": {
|
||||||
|
"node": true,
|
||||||
|
"jest": true
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"@typescript-eslint/interface-name-prefix": "off",
|
||||||
|
"@typescript-eslint/explicit-function-return-type": "off",
|
||||||
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
|
"@typescript-eslint/no-unused-vars": "off"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,9 +1,15 @@
|
|||||||
import { Controller, Get, Post, Body, Param, Options, Header, Delete, HttpCode } from '@nestjs/common';
|
import { Controller, Get, Post, Body, Param, Options, Header, Delete, HttpCode, Put } from '@nestjs/common';
|
||||||
import {StoreService} from './store.service';
|
import {StoreService} from './store.service';
|
||||||
import {Store, StoreResponse, StoreRequest} from './store.schema';
|
import {Store, StoreRequest} from './store.schema';
|
||||||
import {ApiResponse} from '@nestjs/swagger';
|
import {ApiResponse} from '@nestjs/swagger';
|
||||||
import {ALLOW_ORIGIN_ALL, ALLOW_METHOD, ALLOW_CREDENTIALS, CONTENT_LENGTH, ALLOW_HEADERS} from 'src/consts';
|
import {ALLOW_ORIGIN_ALL, ALLOW_METHOD, ALLOW_CREDENTIALS, CONTENT_LENGTH, ALLOW_HEADERS} from 'src/consts';
|
||||||
|
|
||||||
|
const prepareStoreToStoreRequest = ({
|
||||||
|
key, value, description, service_name, author
|
||||||
|
}: Store): StoreRequest => ({
|
||||||
|
key, value, description, service_name, author,
|
||||||
|
});
|
||||||
|
|
||||||
@Controller('store')
|
@Controller('store')
|
||||||
export class StoreController {
|
export class StoreController {
|
||||||
constructor(
|
constructor(
|
||||||
@ -15,10 +21,11 @@ export class StoreController {
|
|||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
description: 'Список всех пар ключ-значение',
|
description: 'Список всех пар ключ-значение',
|
||||||
type: [StoreResponse],
|
type: [StoreRequest],
|
||||||
})
|
})
|
||||||
async findAll(): Promise<Store[]> {
|
async findAll(): Promise<StoreRequest[]> {
|
||||||
return this.storeService.findAll();
|
const storeList = await this.storeService.findAll();
|
||||||
|
return storeList.map(prepareStoreToStoreRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(':key')
|
@Get(':key')
|
||||||
@ -26,21 +33,35 @@ export class StoreController {
|
|||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
description: 'Возвращает пару ключ-значение по ключу',
|
description: 'Возвращает пару ключ-значение по ключу',
|
||||||
type: StoreResponse,
|
type: StoreRequest,
|
||||||
})
|
})
|
||||||
async findOne(@Param() {key}: {key: string}): Promise<Store> {
|
async findOne(@Param() {key}: {key: string}): Promise<StoreRequest> {
|
||||||
return this.storeService.findOne(key);
|
const store = await this.storeService.findOne(key);
|
||||||
|
return prepareStoreToStoreRequest(store);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
@Header(...ALLOW_ORIGIN_ALL)
|
@Header(...ALLOW_ORIGIN_ALL)
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
description: 'Создает новую пару ключ-значение или заменяет существующую по ключу',
|
description: 'Создает новую пару ключ-значение',
|
||||||
type: StoreResponse,
|
type: StoreRequest,
|
||||||
})
|
})
|
||||||
async create(@Body() createStoreClass: StoreRequest): Promise<Store> {
|
async create(@Body() createStoreClass: StoreRequest): Promise<StoreRequest> {
|
||||||
return this.storeService.create(createStoreClass);
|
const store = await this.storeService.create(createStoreClass);
|
||||||
|
return prepareStoreToStoreRequest(store);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Put()
|
||||||
|
@Header(...ALLOW_ORIGIN_ALL)
|
||||||
|
@ApiResponse({
|
||||||
|
status: 200,
|
||||||
|
description: 'Обновляет по ключу объект и мета-поля, кроме author',
|
||||||
|
type: StoreRequest,
|
||||||
|
})
|
||||||
|
async update(@Body() updateStoreClass: StoreRequest): Promise<StoreRequest> {
|
||||||
|
const store = await this.storeService.update(updateStoreClass);
|
||||||
|
return prepareStoreToStoreRequest(store);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Delete(':key')
|
@Delete(':key')
|
||||||
@ -48,10 +69,11 @@ export class StoreController {
|
|||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
description: 'Удаляет пару ключ-значение по ключу',
|
description: 'Удаляет пару ключ-значение по ключу',
|
||||||
type: String,
|
type: StoreRequest,
|
||||||
})
|
})
|
||||||
async removeOne(@Param() {key}: {key: string}): Promise<string> {
|
async removeOne(@Param() {key}: {key: string}): Promise<StoreRequest> {
|
||||||
return this.storeService.removeOne(key);
|
const store = await this.storeService.removeOne(key);
|
||||||
|
return prepareStoreToStoreRequest(store);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Options()
|
@Options()
|
||||||
|
|||||||
@ -8,19 +8,15 @@ export class StoreRequest {
|
|||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
value: any;
|
value: any;
|
||||||
}
|
|
||||||
export class StoreResponse {
|
|
||||||
@ApiProperty()
|
|
||||||
key: string;
|
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
value: any;
|
description: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
_id: string;
|
service_name: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
__v: number;
|
author: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Schema()
|
@Schema()
|
||||||
@ -37,6 +33,24 @@ export class Store extends Document {
|
|||||||
type: {}
|
type: {}
|
||||||
})
|
})
|
||||||
value: any;
|
value: any;
|
||||||
|
|
||||||
|
@Prop({
|
||||||
|
required: true,
|
||||||
|
type: String,
|
||||||
|
})
|
||||||
|
description: string;
|
||||||
|
|
||||||
|
@Prop({
|
||||||
|
required: true,
|
||||||
|
type: String,
|
||||||
|
})
|
||||||
|
service_name: string;
|
||||||
|
|
||||||
|
@Prop({
|
||||||
|
required: true,
|
||||||
|
type: String,
|
||||||
|
})
|
||||||
|
author: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const StoreSchema = SchemaFactory.createForClass(Store);
|
export const StoreSchema = SchemaFactory.createForClass(Store);
|
||||||
|
|||||||
@ -1,8 +1,15 @@
|
|||||||
import {Model} from 'mongoose';
|
import {Model} from 'mongoose';
|
||||||
import {Injectable, NotFoundException} from '@nestjs/common';
|
import {Injectable, NotFoundException, BadGatewayException} from '@nestjs/common';
|
||||||
import {InjectModel} from '@nestjs/mongoose';
|
import {InjectModel} from '@nestjs/mongoose';
|
||||||
import {Store, StoreRequest} from './store.schema';
|
import {Store, StoreRequest} from './store.schema';
|
||||||
|
|
||||||
|
const validateModel = async (store: Store) => {
|
||||||
|
try {
|
||||||
|
await store.validate();
|
||||||
|
} catch (e) {
|
||||||
|
throw new BadGatewayException(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class StoreService {
|
export class StoreService {
|
||||||
@ -14,25 +21,51 @@ export class StoreService {
|
|||||||
|
|
||||||
async create(store: StoreRequest): Promise<Store> {
|
async create(store: StoreRequest): Promise<Store> {
|
||||||
const searchStore = await this.findOne(store.key);
|
const searchStore = await this.findOne(store.key);
|
||||||
|
|
||||||
if (searchStore) {
|
if (searchStore) {
|
||||||
return searchStore.updateOne(store);
|
throw new NotFoundException(`Api key ${store.key} is already taken`);
|
||||||
} else {
|
|
||||||
const createdStore = new this.storeModel(store);
|
|
||||||
return createdStore.save();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const createdStore = new this.storeModel(store);
|
||||||
|
|
||||||
|
await validateModel(createdStore);
|
||||||
|
|
||||||
|
return createdStore.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
async update({author, ...omitProps}: StoreRequest): Promise<Store> {
|
||||||
|
const searchStore = await this.findOne(omitProps.key);
|
||||||
|
|
||||||
|
if (searchStore) {
|
||||||
|
const store = {
|
||||||
|
...omitProps,
|
||||||
|
author: searchStore.author,
|
||||||
|
};
|
||||||
|
const updateStore = new this.storeModel(store);
|
||||||
|
await validateModel(updateStore);
|
||||||
|
|
||||||
|
await searchStore.updateOne({
|
||||||
|
...store,
|
||||||
|
author: searchStore.author,
|
||||||
|
});
|
||||||
|
|
||||||
|
return updateStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new NotFoundException(`Not Found api key - ${omitProps.key}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
async findOne(key: string): Promise<Store> {
|
async findOne(key: string): Promise<Store> {
|
||||||
return this.storeModel.findOne({key});
|
return this.storeModel.findOne({key})
|
||||||
}
|
}
|
||||||
|
|
||||||
async removeOne(key: string): Promise<string> {
|
async removeOne(key: string): Promise<Store> {
|
||||||
const searchStore = await this.findOne(key);
|
const searchStore = await this.findOne(key);
|
||||||
|
|
||||||
if (searchStore) {
|
if (searchStore) {
|
||||||
await searchStore.remove();
|
await this.storeModel.deleteOne({key});
|
||||||
return 'ok';
|
|
||||||
|
return searchStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new NotFoundException(`Not Found key - ${key}`);
|
throw new NotFoundException(`Not Found key - ${key}`);
|
||||||
|
|||||||
25
store.http
25
store.http
@ -6,15 +6,34 @@ GET http://localhost:4001/store/testApi HTTP/1.1
|
|||||||
|
|
||||||
###
|
###
|
||||||
|
|
||||||
POST http://vigdorov.ru:4001/store HTTP/1.1
|
POST http://localhost:4001/store HTTP/1.1
|
||||||
content-type: application/json
|
content-type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"key": "testApi",
|
"key": "testApi",
|
||||||
"value": {
|
"value": {
|
||||||
"name": "ivan",
|
"name": "ivan",
|
||||||
"age": 15
|
"age": 16
|
||||||
}
|
},
|
||||||
|
"description": "тестовое апи",
|
||||||
|
"service_name": "test-api",
|
||||||
|
"author": "vigdorov"
|
||||||
|
}
|
||||||
|
|
||||||
|
### Update Request
|
||||||
|
|
||||||
|
PUT http://localhost:4001/store HTTP/1.1
|
||||||
|
content-type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"key": "testApi",
|
||||||
|
"value": {
|
||||||
|
"name": "ivan23",
|
||||||
|
"age": 22
|
||||||
|
},
|
||||||
|
"description": "тестовое апи",
|
||||||
|
"service_name": "test-api",
|
||||||
|
"author": "vigdorov23422"
|
||||||
}
|
}
|
||||||
|
|
||||||
###
|
###
|
||||||
|
|||||||
Reference in New Issue
Block a user