From 89cf9d0c84f139522c2ffc180527eebc30f53960 Mon Sep 17 00:00:00 2001 From: vigdorov Date: Fri, 10 Jul 2020 15:55:10 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A0=D0=B0=D0=B7=D0=B4=D0=B5=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=BC=D0=B5=D1=82=D0=BE=D0=B4=D0=BE=D0=B2?= =?UTF-8?q?=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0=D0=BD=D0=B8=D1=8F=20=D0=B8=20?= =?UTF-8?q?=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D1=8F=20?= =?UTF-8?q?=D1=8D=D0=BB=D0=B5=D0=BC=D0=B5=D0=BD=D1=82=D0=BE=D0=B2,=20?= =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20?= =?UTF-8?q?=D0=BF=D0=BE=D0=BB=D0=B5=D0=B9=20author,=20description,=20servi?= =?UTF-8?q?ce=5Fname.=20=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=B1=D0=B0=D0=B3=D0=B0=20=D0=BF=D1=80?= =?UTF-8?q?=D0=B8=20=D0=BA=D0=BE=D1=82=D0=BE=D1=80=D0=BE=D0=BC=20=D0=BD?= =?UTF-8?q?=D0=B5=20=D1=83=D0=B4=D0=B0=D0=BB=D1=8F=D0=BB=D0=BE=D1=81=D1=8C?= =?UTF-8?q?=20=D0=B0=D0=BF=D0=B8.=20=D0=A3=D0=B4=D0=B0=D0=BB=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=20=D0=B8=D0=B7=20=D0=BE=D1=82=D0=B2=D0=B5=D1=82?= =?UTF-8?q?=D0=B0=20=D0=BB=D0=B8=D1=88=D0=BD=D0=B8=D1=85=20=D0=BF=D0=BE?= =?UTF-8?q?=D0=BB=D0=B5=D0=B9=20=5F=5Fv=20=D0=B8=20=5Fid?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .eslintrc.js | 24 ---------------- .eslintrc.json | 25 +++++++++++++++++ src/store/store.controller.ts | 52 ++++++++++++++++++++++++---------- src/store/store.schema.ts | 28 +++++++++++++----- src/store/store.service.ts | 53 ++++++++++++++++++++++++++++------- store.http | 25 +++++++++++++++-- 6 files changed, 148 insertions(+), 59 deletions(-) delete mode 100644 .eslintrc.js create mode 100644 .eslintrc.json diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 0ae17ca..0000000 --- a/.eslintrc.js +++ /dev/null @@ -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', - }, -}; diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..420e257 --- /dev/null +++ b/.eslintrc.json @@ -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" + } +} diff --git a/src/store/store.controller.ts b/src/store/store.controller.ts index b132ecc..a321ca8 100644 --- a/src/store/store.controller.ts +++ b/src/store/store.controller.ts @@ -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 {Store, StoreResponse, StoreRequest} from './store.schema'; +import {Store, StoreRequest} from './store.schema'; import {ApiResponse} from '@nestjs/swagger'; 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') export class StoreController { constructor( @@ -15,10 +21,11 @@ export class StoreController { @ApiResponse({ status: 200, description: 'Список всех пар ключ-значение', - type: [StoreResponse], + type: [StoreRequest], }) - async findAll(): Promise { - return this.storeService.findAll(); + async findAll(): Promise { + const storeList = await this.storeService.findAll(); + return storeList.map(prepareStoreToStoreRequest); } @Get(':key') @@ -26,21 +33,35 @@ export class StoreController { @ApiResponse({ status: 200, description: 'Возвращает пару ключ-значение по ключу', - type: StoreResponse, + type: StoreRequest, }) - async findOne(@Param() {key}: {key: string}): Promise { - return this.storeService.findOne(key); + async findOne(@Param() {key}: {key: string}): Promise { + const store = await this.storeService.findOne(key); + return prepareStoreToStoreRequest(store); } @Post() @Header(...ALLOW_ORIGIN_ALL) @ApiResponse({ status: 200, - description: 'Создает новую пару ключ-значение или заменяет существующую по ключу', - type: StoreResponse, + description: 'Создает новую пару ключ-значение', + type: StoreRequest, }) - async create(@Body() createStoreClass: StoreRequest): Promise { - return this.storeService.create(createStoreClass); + async create(@Body() createStoreClass: StoreRequest): Promise { + 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 { + const store = await this.storeService.update(updateStoreClass); + return prepareStoreToStoreRequest(store); } @Delete(':key') @@ -48,10 +69,11 @@ export class StoreController { @ApiResponse({ status: 200, description: 'Удаляет пару ключ-значение по ключу', - type: String, + type: StoreRequest, }) - async removeOne(@Param() {key}: {key: string}): Promise { - return this.storeService.removeOne(key); + async removeOne(@Param() {key}: {key: string}): Promise { + const store = await this.storeService.removeOne(key); + return prepareStoreToStoreRequest(store); } @Options() diff --git a/src/store/store.schema.ts b/src/store/store.schema.ts index 3a72d6b..83a47b9 100644 --- a/src/store/store.schema.ts +++ b/src/store/store.schema.ts @@ -8,19 +8,15 @@ export class StoreRequest { @ApiProperty() value: any; -} -export class StoreResponse { - @ApiProperty() - key: string; @ApiProperty() - value: any; + description: string; @ApiProperty() - _id: string; + service_name: string; @ApiProperty() - __v: number; + author: string; } @Schema() @@ -37,6 +33,24 @@ export class Store extends Document { type: {} }) 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); diff --git a/src/store/store.service.ts b/src/store/store.service.ts index 7f4bab7..1bd905e 100644 --- a/src/store/store.service.ts +++ b/src/store/store.service.ts @@ -1,8 +1,15 @@ import {Model} from 'mongoose'; -import {Injectable, NotFoundException} from '@nestjs/common'; +import {Injectable, NotFoundException, BadGatewayException} from '@nestjs/common'; import {InjectModel} from '@nestjs/mongoose'; import {Store, StoreRequest} from './store.schema'; +const validateModel = async (store: Store) => { + try { + await store.validate(); + } catch (e) { + throw new BadGatewayException(e); + } +}; @Injectable() export class StoreService { @@ -14,25 +21,51 @@ export class StoreService { async create(store: StoreRequest): Promise { const searchStore = await this.findOne(store.key); - + if (searchStore) { - return searchStore.updateOne(store); - } else { - const createdStore = new this.storeModel(store); - return createdStore.save(); + throw new NotFoundException(`Api key ${store.key} is already taken`); } + + const createdStore = new this.storeModel(store); + + await validateModel(createdStore); + + return createdStore.save(); + } + + async update({author, ...omitProps}: StoreRequest): Promise { + 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 { - return this.storeModel.findOne({key}); + return this.storeModel.findOne({key}) } - async removeOne(key: string): Promise { + async removeOne(key: string): Promise { const searchStore = await this.findOne(key); if (searchStore) { - await searchStore.remove(); - return 'ok'; + await this.storeModel.deleteOne({key}); + + return searchStore; } throw new NotFoundException(`Not Found key - ${key}`); diff --git a/store.http b/store.http index 727d7d1..326d504 100644 --- a/store.http +++ b/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 { "key": "testApi", "value": { "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" } ###