From 438d361e2b8b4c2a47875ecf6a177680d1e67407 Mon Sep 17 00:00:00 2001 From: Ilusha92 Date: Fri, 2 Jun 2023 15:43:38 +0300 Subject: [PATCH 1/6] Comments --- Postman/ewm-stat-service.json | 515 ----- Postman/feature.json | 1650 +++++++++++++++++ .../main_service/MainCommonUtils.java | 4 +- .../controller/CommentAdminController.java | 36 + .../controller/CommentPrivateController.java | 57 + .../controller/CommentPublicController.java | 37 + .../main_service/comment/dto/CommentDto.java | 27 + .../comment/dto/NewCommentDto.java | 20 + .../comment/mapper/CommentMapper.java | 13 + .../main_service/comment/model/Comment.java | 43 + .../comment/repository/CommentRepository.java | 15 + .../comment/service/CommentService.java | 25 + .../comment/service/CommentServiceImpl.java | 154 ++ main-service/src/main/resources/schema.sql | 9 + 14 files changed, 2088 insertions(+), 517 deletions(-) delete mode 100644 Postman/ewm-stat-service.json create mode 100644 Postman/feature.json create mode 100644 main-service/src/main/java/ru/practicum/main_service/comment/controller/CommentAdminController.java create mode 100644 main-service/src/main/java/ru/practicum/main_service/comment/controller/CommentPrivateController.java create mode 100644 main-service/src/main/java/ru/practicum/main_service/comment/controller/CommentPublicController.java create mode 100644 main-service/src/main/java/ru/practicum/main_service/comment/dto/CommentDto.java create mode 100644 main-service/src/main/java/ru/practicum/main_service/comment/dto/NewCommentDto.java create mode 100644 main-service/src/main/java/ru/practicum/main_service/comment/mapper/CommentMapper.java create mode 100644 main-service/src/main/java/ru/practicum/main_service/comment/model/Comment.java create mode 100644 main-service/src/main/java/ru/practicum/main_service/comment/repository/CommentRepository.java create mode 100644 main-service/src/main/java/ru/practicum/main_service/comment/service/CommentService.java create mode 100644 main-service/src/main/java/ru/practicum/main_service/comment/service/CommentServiceImpl.java diff --git a/Postman/ewm-stat-service.json b/Postman/ewm-stat-service.json deleted file mode 100644 index 6707a74..0000000 --- a/Postman/ewm-stat-service.json +++ /dev/null @@ -1,515 +0,0 @@ -{ - "info": { - "_postman_id": "2727ee6b-c606-49ec-9d4b-549c21dbe7ae", - "name": "Tests for detatched stats service", - "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", - "_exporter_id": "23073145" - }, - "item": [ - { - "name": "Получение статистики по посещениям. (Тест на опциональность параметра uris)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try { \r", - " pm.collectionVariables.set(\"uri\", '/events'); \r", - " let post1 = rnd.getPost();\r", - " let post2 = rnd.getPost();\r", - " post1['uri'] = '/events';\r", - " post2['uri'] = '/events/5';\r", - " await api.addPost(post1);\r", - " await api.addPost(post2);\r", - "\r", - " let source = await api.getPosts(['/events']);\r", - " pm.collectionVariables.set('source', source);\r", - " \r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Ответ должен содержать код статуса 200 и данные в формате json при запросе без опционального параметра uris\", function () {\r", - " pm.response.to.be.ok; \r", - " pm.response.to.be.withBody;\r", - " pm.response.to.be.json;\r", - "});\r", - "\r", - "const target = pm.response.json();\r", - "const source = pm.collectionVariables.get('source');\r", - "\r", - "pm.test(\"При запросе по конкретному uris должны получить 1 запись\", function () {\r", - " pm.expect(source.length).to.equal(1);\r", - "});\r", - "\r", - "pm.test(\"При запросе без uris должны получить больше 1 записи\", function () {\r", - " pm.expect(target.length).to.be.above(1);\r", - "});\r", - "\r", - "pm.test(\"Посты должны содержать поля: app, uri, hits\", function () {\r", - " pm.expect(target[0]).to.have.all.keys('app', 'uri', 'hits');\r", - " pm.expect(target[1]).to.have.all.keys('app', 'uri', 'hits');\r", - " pm.expect(source[0]).to.have.all.keys('app', 'uri', 'hits');\r", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Accept", - "value": "application/json" - } - ], - "url": { - "raw": "{{baseUrl}}/stats?start=2020-05-05 00:00:00&end=2035-05-05 00:00:00&unique=false", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "stats" - ], - "query": [ - { - "key": "start", - "value": "2020-05-05 00:00:00", - "description": "(Required) Дата и время начала диапазона за который нужно выгрузить статистику (в формате \"yyyy-MM-dd HH:mm:ss\")" - }, - { - "key": "end", - "value": "2035-05-05 00:00:00", - "description": "(Required) Дата и время конца диапазона за который нужно выгрузить статистику (в формате \"yyyy-MM-dd HH:mm:ss\")" - }, - { - "key": "uris", - "value": "{{uri}}", - "description": "Список uri для которых нужно выгрузить статистику", - "disabled": true - }, - { - "key": "uris", - "value": "aliqua o", - "description": "Список uri для которых нужно выгрузить статистику", - "disabled": true - }, - { - "key": "unique", - "value": "false", - "description": "Нужно ли учитывать только уникальные посещения (только с уникальным ip)" - } - ] - } - }, - "response": [] - }, - { - "name": "Получение статистики по посещениям. (Тест на опциональность и работу параметра unique)", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try { \r", - " pm.collectionVariables.set(\"uri\", '/events'); \r", - " let post = rnd.getPost();\r", - " post['uri'] = '/events';\r", - " await api.addPost(post);\r", - " await api.addPost(post);\r", - " await api.addPost(post);\r", - " \r", - " let source = await api.getPosts(['/events']);\r", - " pm.collectionVariables.set('source', source);\r", - "\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Ответ должен содержать код статуса 200 и данные в формате json при запросе без опционального параметра unique\", function () {\r", - " pm.response.to.be.ok; \r", - " pm.response.to.be.withBody;\r", - " pm.response.to.be.json;\r", - "});\r", - "\r", - "const target = pm.response.json()[0];\r", - "const source = pm.collectionVariables.get('source')[0];\r", - "\r", - "pm.test(\"При запросе с unique==true должен быть всего 1 уникальный запрос\", function () {\r", - " pm.expect(target.hits).to.equal(1);\r", - "});\r", - "\r", - "pm.test(\"При запросе без uniqre должны получить минимум 3 запроса(поскольку делали 3)\", function () {\r", - " pm.expect(source.hits).to.be.above(2);\r", - "});\r", - "\r", - "pm.test(\"Посты должны содержать поля: app, uri, hits\", function () {\r", - " pm.expect(target).to.have.all.keys('app', 'uri', 'hits');\r", - " pm.expect(source).to.have.all.keys('app', 'uri', 'hits');\r", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Accept", - "value": "application/json" - } - ], - "url": { - "raw": "{{baseUrl}}/stats?start=2020-05-05 00:00:00&end=2035-05-05 00:00:00&uris={{uri}}&unique=true", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "stats" - ], - "query": [ - { - "key": "start", - "value": "2020-05-05 00:00:00", - "description": "(Required) Дата и время начала диапазона за который нужно выгрузить статистику (в формате \"yyyy-MM-dd HH:mm:ss\")" - }, - { - "key": "end", - "value": "2035-05-05 00:00:00", - "description": "(Required) Дата и время конца диапазона за который нужно выгрузить статистику (в формате \"yyyy-MM-dd HH:mm:ss\")" - }, - { - "key": "uris", - "value": "{{uri}}", - "description": "Список uri для которых нужно выгрузить статистику" - }, - { - "key": "uris", - "value": "aliqua o", - "description": "Список uri для которых нужно выгрузить статистику", - "disabled": true - }, - { - "key": "unique", - "value": "true", - "description": "Нужно ли учитывать только уникальные посещения (только с уникальным ip)" - } - ] - } - }, - "response": [] - }, - { - "name": "Тест корреткной работы сохранения и просмотра количества просмотров", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " pm.collectionVariables.set(\"uri\", '/events/1&uris=/events/2');\r", - " let post1 = rnd.getPost();\r", - " let post2 = rnd.getPost();\r", - " post1['uri'] = '/events/1';\r", - " post2['uri'] = '/events/2';\r", - " await api.addPost(post1);\r", - " await api.addPost(post2);\r", - " await api.addPost(post2);\r", - " let source = await api.getPosts(['/events/1', '/events/2']);\r", - " await api.addPost(post1);\r", - " await api.addPost(post2);\r", - " pm.collectionVariables.set('source', source);\r", - " \r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Ответ должен содержать код статуса 200 и данные в формате json\", function () {\r", - " pm.response.to.be.ok; \r", - " pm.response.to.be.withBody;\r", - " pm.response.to.be.json;\r", - "});\r", - "\r", - "const target = pm.response.json();\r", - "const source = pm.collectionVariables.get('source');\r", - "\r", - "\r", - "pm.test(\"Посты должны содержать поля: app, uri, hits\", function () {\r", - " pm.expect(target[0]).to.have.all.keys('app', 'uri', 'hits');\r", - " pm.expect(target[1]).to.have.all.keys('app', 'uri', 'hits');\r", - "});\r", - "\r", - "pm.test(\"В теле ответа должна соблюдаться сортировка по убыванию количества просмотров\", function(){\r", - " pm.expect(target[0].hits).to.be.above(target[1].hits);\r", - "});\r", - "\r", - "pm.test(\"Проверка соответствия реального количества просмотров событий и сохраненных хитов\", function(){\r", - " pm.expect(source[0].hits+1).equal(target[0].hits);\r", - " pm.expect(source[1].hits+1).equal(target[1].hits);\r", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "Accept", - "value": "application/json" - } - ], - "url": { - "raw": "{{baseUrl}}/stats?start=2020-05-05 00:00:00&end=2035-05-05 00:00:00&uris={{uri}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "stats" - ], - "query": [ - { - "key": "start", - "value": "2020-05-05 00:00:00", - "description": "(Required) Дата и время начала диапазона за который нужно выгрузить статистику (в формате \"yyyy-MM-dd HH:mm:ss\")" - }, - { - "key": "end", - "value": "2035-05-05 00:00:00", - "description": "(Required) Дата и время конца диапазона за который нужно выгрузить статистику (в формате \"yyyy-MM-dd HH:mm:ss\")" - }, - { - "key": "uris", - "value": "{{uri}}", - "description": "Список uri для которых нужно выгрузить статистику" - }, - { - "key": "uris", - "value": "aliqua o", - "description": "Список uri для которых нужно выгрузить статистику", - "disabled": true - }, - { - "key": "unique", - "value": "false", - "description": "Нужно ли учитывать только уникальные посещения (только с уникальным ip)", - "disabled": true - } - ] - } - }, - "response": [] - } - ], - "event": [ - { - "listen": "prerequest", - "script": { - "type": "text/javascript", - "exec": [ - "API = class {", - " constructor(postman, verbose = false, baseUrl = \"http://localhost:9090\") {", - " this.baseUrl = baseUrl;", - " this.pm = postman;", - " this._verbose = verbose;", - " }", - "", - " async addPost(post, verbose=null) {", - " return this.post(\"/hit\", post, \"Ошибка при сохранении информации о запросе к эндпойнту: \", verbose);", - " }", - "", - " async getPosts(uris=null, verbose=null) {", - " return this.get(uris == null ? \"/stats?start=2020-05-05 00:00:00&end=2035-05-05 00:00:00\" : \"/stats?start=2020-05-05 00:00:00&end=2035-05-05 00:00:00&uris=\"+uris.join('&uris='), null, \"Ошибка при сохранении информации о запросе к эндпойнту: \", verbose);", - " }", - "", - " async post(path, body, errorText = \"Ошибка при выполнении post-запроса: \", verbose=null) {", - " return this.sendRequest(\"POST\", path, body, errorText);", - " }", - "", - " async get(path, body = null, errorText = \"Ошибка при выполнении get-запроса: \", verbose=null) {", - " return this.sendRequest(\"GET\", path, body, errorText);", - " }", - "", - " async sendRequest(method, path, body=null, errorText = \"Ошибка при выполнении запроса: \", verbose=null) {", - " return new Promise((resolve, reject) => {", - " verbose = verbose == null ? this._verbose : verbose;", - "", - " let request = {", - " url: this.baseUrl + path,", - " method: method,", - " body: body == null ? \"\" : JSON.stringify(body),", - " header: { \"Content-Type\": \"application/json\" },", - " };", - "", - " if(verbose) {", - " console.log(\"Отправляю запрос: \", request);", - " }", - "", - " try {", - " this.pm.sendRequest(request, (error, response) => {", - " if(error || (response.code >= 400 && response.code <= 599)) {", - " let err = error ? error : JSON.stringify(response.json());", - " console.error(\"При выполнении запроса к серверу возникла ошика.\\n\", err,", - " \"\\nДля отладки проблемы повторите такой же запрос к вашей программе \" + ", - " \"на локальном компьютере. Данные запроса:\\n\", JSON.stringify(request));", - "", - " reject(new Error(errorText + err));", - " }", - "", - " if(verbose) {", - " console.log(\"Результат обработки запроса: код состояния - \", response.code, \", тело: \", response.json());", - " }", - " try{", - " resolve(response.json());", - " } catch(err){", - " resolve(response);", - " }", - " ", - " });", - " } catch(err) {", - " if(verbose) {", - " console.error(errorText, err);", - " }", - " return Promise.reject(err);", - " }", - " });", - " }", - "};", - "", - "RandomUtils = class {", - " constructor() {}", - "", - " getPost() {", - " return {", - " app: \"ewm-main-service\",", - " uri: \"/events/\" + pm.variables.replaceIn('{{$randomInt}}'),", - " ip: '121.0.0.1',", - " timestamp: this.getPastDateTime()", - " }", - " }", - "", - " getPastDateTime(hourShift = 5, minuteShift=0, yearShift=0) {", - " let moment = require('moment');", - "", - " let m = moment();", - " m.subtract(hourShift, 'hour');", - " m.subtract(minuteShift, 'minute');", - " m.subtract(yearShift, 'year');", - "", - " return m.format('YYYY-MM-DD HH:mm:ss');", - " }", - "", - "}" - ] - } - }, - { - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "" - ] - } - } - ], - "variable": [ - { - "key": "baseUrl", - "value": "http://localhost:9090", - "type": "string" - }, - { - "key": "uri", - "value": "1" - }, - { - "key": "source", - "value": "" - } - ] -} diff --git a/Postman/feature.json b/Postman/feature.json new file mode 100644 index 0000000..403637b --- /dev/null +++ b/Postman/feature.json @@ -0,0 +1,1650 @@ +{ + "info": { + "_postman_id": "506d43c6-b3fd-4fad-bd54-d5d902ce7a8a", + "name": "3 Feature - Comments", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "24124777" + }, + "item": [ + { + "name": "Create comment by user", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 201 и данные в формате json\", function () {\r", + " pm.response.to.have.status(201);\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "\r", + "const source = JSON.parse(pm.request.body.raw);\r", + "const target = pm.response.json();\r", + "\r", + "pm.test(\"Комментарий должен содержать поля: id, text, author, eventId, createdOn, editedOn\", function () {\r", + " pm.expect(target).to.have.property('id');\r", + " pm.expect(target).to.have.property('text');\r", + " pm.expect(target).to.have.property('author');\r", + " pm.expect(target).to.have.property('eventId');\r", + " pm.expect(target).to.have.property('createdOn');\r", + " pm.expect(target).to.have.property('editedOn');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(target.id).to.not.be.null;\r", + " pm.expect(target.text).to.be.a(\"string\");\r", + " pm.expect(source.text).equal(target.text, 'Текст комментария должен соответствовать указанному при создании');\r", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let comment;\r", + " try {\r", + " const user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"uid\", user.id)\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " let event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " event = await api.publishEvent(event.id);\r", + " pm.collectionVariables.set(\"eid\", event.id)\r", + " comment = rnd.getComment();\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(comment),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, + { + "key": "Accept", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}" + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/comments?eventId={{eid}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "comments" + ], + "query": [ + { + "key": "eventId", + "value": "{{eid}}" + } + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Create comment failed if event not published", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 409 и данные в формате json\", function () {\r", + " pm.response.to.have.status(409);\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let comment;\r", + " try {\r", + " const user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"uid\", user.id)\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " let event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " pm.collectionVariables.set(\"eid\", event.id)\r", + " comment = rnd.getComment();\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(comment),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, + { + "key": "Accept", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}" + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/comments?eventId={{eid}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "comments" + ], + "query": [ + { + "key": "eventId", + "value": "{{eid}}" + } + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Get event comments by user", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 200 и данные в формате json\", function () {\r", + " pm.response.to.be.ok; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "\r", + "const source = pm.collectionVariables.get('response');\r", + "const target = pm.response.json();\r", + "let founded;\r", + "target.forEach(function(element){if (element.id == source.id) founded = element});\r", + "\r", + "pm.test(\"Комментарий должен содержать поля: id, text, author, eventId, createdOn, editedOn\", function () {\r", + " pm.expect(target[0]).to.have.property('id');\r", + " pm.expect(target[0]).to.have.property('text');\r", + " pm.expect(target[0]).to.have.property('author');\r", + " pm.expect(target[0]).to.have.property('eventId');\r", + " pm.expect(target[0]).to.have.property('createdOn');\r", + " pm.expect(target[0]).to.have.property('editedOn');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(founded.id).to.not.be.null;\r", + " pm.expect(founded.text).to.be.a(\"string\");\r", + " pm.expect(source.text).equal(founded.text, 'Текст комментария должен соответствовать указанному при создании');\r", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " const user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"uid\", user.id)\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " let event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " event = await api.publishEvent(event.id);\r", + " pm.collectionVariables.set(\"eid\", event.id)\r", + " const comment = await api.addComment(user.id, event.id, rnd.getComment());\r", + " pm.collectionVariables.set(\"cid\", comment.id)\r", + " pm.collectionVariables.set(\"response\", comment);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/users/:userId/comments?eventId={{eid}}&from=0&size=1000", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "comments" + ], + "query": [ + { + "key": "eventId", + "value": "{{eid}}" + }, + { + "key": "from", + "value": "0" + }, + { + "key": "size", + "value": "1000" + } + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Get all comments by user", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 200 и данные в формате json\", function () {\r", + " pm.response.to.be.ok; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "\r", + "const source = pm.collectionVariables.get('response');\r", + "const target = pm.response.json();\r", + "let founded;\r", + "target.forEach(function(element){if (element.id == source.id) founded = element});\r", + "\r", + "pm.test(\"Комментарий должен содержать поля: id, text, author, eventId, createdOn, editedOn\", function () {\r", + " pm.expect(target[0]).to.have.property('id');\r", + " pm.expect(target[0]).to.have.property('text');\r", + " pm.expect(target[0]).to.have.property('author');\r", + " pm.expect(target[0]).to.have.property('eventId');\r", + " pm.expect(target[0]).to.have.property('createdOn');\r", + " pm.expect(target[0]).to.have.property('editedOn');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(founded.id).to.not.be.null;\r", + " pm.expect(founded.text).to.be.a(\"string\");\r", + " pm.expect(source.text).equal(founded.text, 'Текст комментария должен соответствовать указанному при создании');\r", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " const user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"uid\", user.id)\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " let event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " event = await api.publishEvent(event.id);\r", + " pm.collectionVariables.set(\"eid\", event.id)\r", + " const comment = await api.addComment(user.id, event.id, rnd.getComment());\r", + " pm.collectionVariables.set(\"cid\", comment.id)\r", + " pm.collectionVariables.set(\"response\", comment);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/users/:userId/comments?from=0&size=1000", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "comments" + ], + "query": [ + { + "key": "from", + "value": "0" + }, + { + "key": "size", + "value": "1000" + } + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Get all comments by admin", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 200 и данные в формате json\", function () {\r", + " pm.response.to.be.ok; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "\r", + "const source = pm.collectionVariables.get('response');\r", + "const target = pm.response.json();\r", + "let founded;\r", + "target.forEach(function(element){if (element.id == source.id) founded = element});\r", + "\r", + "pm.test(\"Комментарий должен содержать поля: id, text, author, eventId, createdOn, editedOn\", function () {\r", + " pm.expect(target[0]).to.have.property('id');\r", + " pm.expect(target[0]).to.have.property('text');\r", + " pm.expect(target[0]).to.have.property('author');\r", + " pm.expect(target[0]).to.have.property('eventId');\r", + " pm.expect(target[0]).to.have.property('createdOn');\r", + " pm.expect(target[0]).to.have.property('editedOn');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(founded.id).to.not.be.null;\r", + " pm.expect(founded.text).to.be.a(\"string\");\r", + " pm.expect(source.text).equal(founded.text, 'Текст комментария должен соответствовать указанному при создании');\r", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " const user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"uid\", user.id)\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " let event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " event = await api.publishEvent(event.id);\r", + " pm.collectionVariables.set(\"eid\", event.id)\r", + " const comment = await api.addComment(user.id, event.id, rnd.getComment());\r", + " pm.collectionVariables.set(\"cid\", comment.id)\r", + " pm.collectionVariables.set(\"response\", comment);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/admin/comments?from=0&size=1000", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "comments" + ], + "query": [ + { + "key": "from", + "value": "0" + }, + { + "key": "size", + "value": "1000" + } + ] + } + }, + "response": [] + }, + { + "name": "Get event comments by public", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 200 и данные в формате json\", function () {\r", + " pm.response.to.be.ok; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "\r", + "const source = pm.collectionVariables.get('response');\r", + "const target = pm.response.json();\r", + "let founded;\r", + "target.forEach(function(element){if (element.id == source.id) founded = element;});\r", + "\r", + "pm.test(\"Комментарий должен содержать поля: id, text, author, eventId, createdOn, editedOn\", function () {\r", + " pm.expect(target[0]).to.have.property('id');\r", + " pm.expect(target[0]).to.have.property('text');\r", + " pm.expect(target[0]).to.have.property('author');\r", + " pm.expect(target[0]).to.have.property('eventId');\r", + " pm.expect(target[0]).to.have.property('createdOn');\r", + " pm.expect(target[0]).to.have.property('editedOn');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(founded.id).to.not.be.null;\r", + " pm.expect(founded.text).to.be.a(\"string\");\r", + " pm.expect(source.text).equal(founded.text, 'Текст комментария должен соответствовать указанному при создании');\r", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " const user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"uid\", user.id)\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " let event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " event = await api.publishEvent(event.id);\r", + " pm.collectionVariables.set(\"eid\", event.id)\r", + " const comment = await api.addComment(user.id, event.id, rnd.getComment());\r", + " pm.collectionVariables.set(\"cid\", comment.id)\r", + " pm.collectionVariables.set(\"response\", comment);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/comments?eventId={{eid}}&from=0&size=1000", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "comments" + ], + "query": [ + { + "key": "eventId", + "value": "{{eid}}" + }, + { + "key": "from", + "value": "0" + }, + { + "key": "size", + "value": "1000" + } + ] + } + }, + "response": [] + }, + { + "name": "Get comment by public", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 200 и данные в формате json\", function () {\r", + " pm.response.to.be.ok; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "\r", + "const source = pm.collectionVariables.get('response');\r", + "const target = pm.response.json();\r", + "\r", + "pm.test(\"Комментарий должен содержать поля: id, text, author, eventId, createdOn, editedOn\", function () {\r", + " pm.expect(target).to.have.property('id');\r", + " pm.expect(target).to.have.property('text');\r", + " pm.expect(target).to.have.property('author');\r", + " pm.expect(target).to.have.property('eventId');\r", + " pm.expect(target).to.have.property('createdOn');\r", + " pm.expect(target).to.have.property('editedOn');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(target.id).to.not.be.null;\r", + " pm.expect(target.text).to.be.a(\"string\");\r", + " pm.expect(target.text).equal(source.text, 'Текст комментария должен соответствовать указанному при создании');\r", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " const user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"uid\", user.id)\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " let event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " event = await api.publishEvent(event.id);\r", + " pm.collectionVariables.set(\"eid\", event.id)\r", + " const comment = await api.addComment(user.id, event.id, rnd.getComment());\r", + " pm.collectionVariables.set(\"cid\", comment.id)\r", + " pm.collectionVariables.set(\"response\", comment);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/comments/:commentId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "comments", + ":commentId" + ], + "variable": [ + { + "key": "commentId", + "value": "{{cid}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Update comment by user", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 200 и данные в формате json\", function () {\r", + " pm.response.to.have.status(200);\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "\r", + "const source = JSON.parse(pm.request.body.raw);\r", + "const target = pm.response.json();\r", + "\r", + "pm.test(\"Комментарий должен содержать поля: id, text, author, eventId, createdOn, editedOn\", function () {\r", + " pm.expect(target).to.have.property('id');\r", + " pm.expect(target).to.have.property('text');\r", + " pm.expect(target).to.have.property('author');\r", + " pm.expect(target).to.have.property('eventId');\r", + " pm.expect(target).to.have.property('createdOn');\r", + " pm.expect(target).to.have.property('editedOn');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(target.id).to.not.be.null;\r", + " pm.expect(target.text).to.be.a(\"string\");\r", + " pm.expect(source.text).equal(target.text, 'Текст комментария должен соответствовать указанному при создании');\r", + " pm.expect(target.editedOn).to.not.be.null;\r", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let comment;\r", + " try {\r", + " const user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"uid\", user.id)\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " let event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " event = await api.publishEvent(event.id);\r", + " pm.collectionVariables.set(\"eid\", event.id)\r", + " comment = await api.addComment(user.id, event.id, rnd.getComment());\r", + " pm.collectionVariables.set(\"cid\", comment.id)\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(rnd.getComment()),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, + { + "key": "Accept", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}" + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/comments/:commentId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "comments", + ":commentId" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}" + }, + { + "key": "commentId", + "value": "{{cid}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Update comment failed if user not owner", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 409 и данные в формате json\", function () {\r", + " pm.response.to.have.status(409);\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let comment;\r", + " try {\r", + " const user1 = await api.addUser(rnd.getUser());\r", + " const user2 = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"uid\", user2.id)\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " let event = await api.addEvent(user1.id, rnd.getEvent(category.id));\r", + " event = await api.publishEvent(event.id);\r", + " pm.collectionVariables.set(\"eid\", event.id)\r", + " comment = await api.addComment(user1.id, event.id, rnd.getComment());\r", + " pm.collectionVariables.set(\"cid\", comment.id)\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(rnd.getComment()),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, + { + "key": "Accept", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}" + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/comments/:commentId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "comments", + ":commentId" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}" + }, + { + "key": "commentId", + "value": "{{cid}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Delete comment by user", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 204\", function () {\r", + " pm.response.to.have.status(204);\r", + "});\r", + "\r", + "let source = pm.collectionVariables.get('response');\r", + "\r", + "pm.test(\"Комментарий должен быть найден до удаления\", function () {\r", + " pm.expect(source).not.to.be.null;\r", + "});\r", + "\r", + "let body\r", + "const req = {\r", + " url: \"http://localhost:8080/admin/comments?from=0&size=1000\",\r", + " method: \"GET\",\r", + " body: body == null ? \"\" : JSON.stringify(body),\r", + " header: { \"Content-Type\": \"application/json\" },\r", + "};\r", + "pm.sendRequest(req, (error, response) => {\r", + " pm.test(\"Комментарий должен быть удален после выполнения запроса\", function(){\r", + " response.json().forEach(element => {\r", + " if(element.id == pm.collectionVariables.get('cid')){\r", + " throw new Error(\"Комментарий все еще находится в списке существующих\");\r", + " }\r", + " })\r", + " });\r", + "})" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " const user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"uid\", user.id)\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " let event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " event = await api.publishEvent(event.id);\r", + " pm.collectionVariables.set(\"eid\", event.id)\r", + " const comment = await api.addComment(user.id, event.id, rnd.getComment());\r", + " pm.collectionVariables.set(\"cid\", comment.id);\r", + " const foundComment = await api.findComment(comment.id);\r", + " pm.collectionVariables.set('response', foundComment);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "DELETE", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, + { + "key": "Accept", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/comments/:commentId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "comments", + ":commentId" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}" + }, + { + "key": "commentId", + "value": "{{cid}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Delete comment failed if user not owner", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 409\", function () {\r", + " pm.response.to.have.status(409);\r", + "});" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " const user1 = await api.addUser(rnd.getUser());\r", + " const user2 = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"uid\", user2.id)\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " let event = await api.addEvent(user1.id, rnd.getEvent(category.id));\r", + " event = await api.publishEvent(event.id);\r", + " pm.collectionVariables.set(\"eid\", event.id)\r", + " const comment = await api.addComment(user1.id, event.id, rnd.getComment());\r", + " pm.collectionVariables.set(\"cid\", comment.id);\r", + " const foundComment = await api.findComment(comment.id);\r", + " pm.collectionVariables.set('response', foundComment);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "DELETE", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, + { + "key": "Accept", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/comments/:commentId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "comments", + ":commentId" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}" + }, + { + "key": "commentId", + "value": "{{cid}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Delete comment by admin", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 204\", function () {\r", + " pm.response.to.have.status(204);\r", + "});\r", + "\r", + "let source = pm.collectionVariables.get('response');\r", + "\r", + "pm.test(\"Комментарий должен быть найден до удаления\", function () {\r", + " pm.expect(source).not.to.be.null;\r", + "});\r", + "\r", + "let body\r", + "const req = {\r", + " url: \"http://localhost:8080/admin/comments?from=0&size=1000\",\r", + " method: \"GET\",\r", + " body: body == null ? \"\" : JSON.stringify(body),\r", + " header: { \"Content-Type\": \"application/json\" },\r", + "};\r", + "pm.sendRequest(req, (error, response) => {\r", + " pm.test(\"Комментарий должен быть удален после выполнения запроса\", function(){\r", + " response.json().forEach(element => {\r", + " if(element.id == pm.collectionVariables.get('cid')){\r", + " throw new Error(\"Комментарий все еще находится в списке существующих\");\r", + " }\r", + " })\r", + " });\r", + "})" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " const user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"uid\", user.id)\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " let event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " event = await api.publishEvent(event.id);\r", + " pm.collectionVariables.set(\"eid\", event.id)\r", + " const comment = await api.addComment(user.id, event.id, rnd.getComment());\r", + " pm.collectionVariables.set(\"cid\", comment.id);\r", + " const foundComment = await api.findComment(comment.id);\r", + " pm.collectionVariables.set('response', foundComment);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "DELETE", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, + { + "key": "Accept", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{baseUrl}}/admin/comments/:commentId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "comments", + ":commentId" + ], + "variable": [ + { + "key": "commentId", + "value": "{{cid}}" + } + ] + } + }, + "response": [] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "API = class {", + " constructor(postman, verbose = false, baseUrl = \"http://localhost:8080\") {", + " this.baseUrl = baseUrl;", + " this.pm = postman;", + " this._verbose = verbose;", + " }", + "", + " async addUser(user, verbose=null) {", + " return this.post(\"/admin/users\", user, \"Ошибка при добавлении нового пользователя: \", verbose);", + " }", + "", + " async addCategory(category, verbose=null) {", + " return this.post(\"/admin/categories\", category, \"Ошибка при добавлении новой категории: \", verbose);", + " }", + "", + " async addEvent(userId, event, verbose=null) {", + " return this.post(\"/users/\" + userId + \"/events\", event, \"Ошибка при добавлении нового события: \", verbose);", + " }", + "", + " async addCompilation(compilation, verbose=null) {", + " return this.post(\"/admin/compilations\", compilation, \"Ошибка при добавлении новой подборки: \", verbose);", + " }", + "", + " async publishParticipationRequest(eventId, userId, verbose=null) {", + " return this.post('/users/' + userId + '/requests?eventId=' + eventId, null, \"Ошибка при добавлении нового запроса на участие в событии\", verbose);", + " }", + "", + " async publishEvent(eventId, verbose=null) {", + " return this.patch('/admin/events/' + eventId, {stateAction: \"PUBLISH_EVENT\"}, \"Ошибка при публикации события\", verbose);", + " }", + " ", + " async rejectEvent(eventId, verbose=null) {", + " return this.patch('/admin/events/' + eventId, {stateAction: \"REJECT_EVENT\"}, \"Ошибка при отмене события\", verbose);", + " }", + "", + " async acceptParticipationRequest(eventId, userId, reqId, verbose=null) {", + " return this.patch('/users/' + userId + '/events/' + eventId + '/requests/', {requestIds:[reqId], status: \"CONFIRMED\"}, \"Ошибка при принятии заявки на участие в событии\", verbose);", + " }", + "", + " async findCategory(catId, verbose=null) {", + " return this.get('/categories/' + catId, null, \"Ошибка при поиске категории по id\", verbose);", + " }", + "", + " async findCompilation(compId, verbose=null) {", + " return this.get('/compilations/' + compId, null, \"Ошибка при поиске подборки по id\", verbose);", + " }", + "", + " async findUser(userId, verbose=null) {", + " return this.get('/admin/users?ids=' + userId, null, \"Ошибка при поиске пользователя по id\", verbose);", + " }", + "", + " async addComment(userId, eventId, comment, verbose=null) {", + " return this.post('/users/' + userId + '/comments' + '?eventId=' + eventId, comment, \"Ошибка при добавлении нового комментария к событию\", verbose);", + " }", + "", + " async findComment(commentId, verbose=null) {", + " return this.get('/comments/' + commentId, null, \"Ошибка при поиске комментария по id\", verbose);", + " }", + "", + " async post(path, body, errorText = \"Ошибка при выполнении post-запроса: \", verbose=null) {", + " return this.sendRequest(\"POST\", path, body, errorText, verbose);", + " }", + "", + " async patch(path, body = null, errorText = \"Ошибка при выполнении patch-запроса: \", verbose=null) {", + " return this.sendRequest(\"PATCH\", path, body, errorText, verbose);", + " }", + "", + " async get(path, body = null, errorText = \"Ошибка при выполнении get-запроса: \", verbose=null) {", + " return this.sendRequest(\"GET\", path, body, errorText, verbose);", + " }", + "", + " async sendRequest(method, path, body=null, errorText = \"Ошибка при выполнении запроса: \", verbose=null) {", + " return new Promise((resolve, reject) => {", + " verbose = verbose == null ? this._verbose : verbose;", + "", + " const request = {", + " url: this.baseUrl + path,", + " method: method,", + " body: body == null ? \"\" : JSON.stringify(body),", + " header: { \"Content-Type\": \"application/json\" },", + " };", + "", + " if(verbose) {", + " console.log(\"Отправляю запрос: \", request);", + " }", + "", + " try {", + " this.pm.sendRequest(request, (error, response) => {", + " if(error || (response.code >= 400 && response.code <= 599)) {", + " let err = error ? error : JSON.stringify(response.json());", + " console.error(\"При выполнении запроса к серверу возникла ошика.\\n\", err,", + " \"\\nДля отладки проблемы повторите такой же запрос к вашей программе \" + ", + " \"на локальном компьютере. Данные запроса:\\n\", JSON.stringify(request));", + "", + " reject(new Error(errorText + err));", + " }", + "", + " if(verbose) {", + " console.log(\"Результат обработки запроса: код состояния - \", response.code, \", тело: \", response.json());", + " }", + "", + " resolve(response.json());", + " });", + " } catch(err) {", + " if(verbose) {", + " console.error(errorText, err);", + " }", + " return Promise.reject(err);", + " }", + " });", + " }", + "};", + "", + "RandomUtils = class {", + " constructor() {}", + "", + " getUser() {", + " return {", + " name: pm.variables.replaceIn('{{$randomFullName}}'),", + " email: pm.variables.replaceIn('{{$randomEmail}}')", + " };", + " }", + "", + " getCategory() {", + " return {", + " name: pm.variables.replaceIn('{{$randomWord}}') + Math.floor(Math.random() * 100).toString()", + " };", + " }", + "", + " getEvent(categoryId) {", + " return {", + " annotation: pm.variables.replaceIn('{{$randomLoremParagraph}}'),", + " category: categoryId,", + " description: pm.variables.replaceIn('{{$randomLoremParagraphs}}'),", + " eventDate: this.getFutureDateTime(),", + " location: {", + " lat: parseFloat(pm.variables.replaceIn('{{$randomLatitude}}')),", + " lon: parseFloat(pm.variables.replaceIn('{{$randomLongitude}}')),", + " },", + " paid: pm.variables.replaceIn('{{$randomBoolean}}'),", + " participantLimit: pm.variables.replaceIn('{{$randomInt}}'),", + " requestModeration: pm.variables.replaceIn('{{$randomBoolean}}'),", + " title: pm.variables.replaceIn('{{$randomLoremSentence}}'),", + " }", + " }", + "", + " getComment() {", + " return {", + " text: pm.variables.replaceIn('{{$randomLoremSentence}}')", + " };", + " }", + "", + " getCompilation(...eventIds) {", + " return {", + " title: pm.variables.replaceIn('{{$randomLoremSentence}}'),", + " pinned: pm.variables.replaceIn('{{$randomBoolean}}'),", + " events: eventIds", + " };", + " }", + "", + " getFutureDateTime(hourShift = 5, minuteShift=0, yearShift=0) {", + " let moment = require('moment');", + "", + " let m = moment();", + " m.add(hourShift, 'hour');", + " m.add(minuteShift, 'minute');", + " m.add(yearShift, 'year');", + "", + " return m.format('YYYY-MM-DD HH:mm:ss');", + " }", + "}" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ], + "variable": [ + { + "key": "baseUrl", + "value": "http://localhost:8080", + "type": "string" + }, + { + "key": "name", + "value": "" + }, + { + "key": "usersCount", + "value": 1, + "type": "number" + }, + { + "key": "catid", + "value": 1, + "type": "number" + }, + { + "key": "request_body", + "value": "" + }, + { + "key": "mail", + "value": "" + }, + { + "key": "response", + "value": "" + }, + { + "key": "uid", + "value": 1, + "type": "number" + }, + { + "key": "catname", + "value": "" + }, + { + "key": "eid", + "value": 1, + "type": "number" + }, + { + "key": "compid", + "value": 1, + "type": "number" + }, + { + "key": "toCheck", + "value": "" + }, + { + "key": "newDataToSet", + "value": "" + }, + { + "key": "uid1", + "value": "" + }, + { + "key": "reqid", + "value": 1, + "type": "number" + }, + { + "key": "catId", + "value": "" + }, + { + "key": "cid", + "value": "1", + "type": "string" + } + ] +} \ No newline at end of file diff --git a/main-service/src/main/java/ru/practicum/main_service/MainCommonUtils.java b/main-service/src/main/java/ru/practicum/main_service/MainCommonUtils.java index 7721b92..bebfe0c 100644 --- a/main-service/src/main/java/ru/practicum/main_service/MainCommonUtils.java +++ b/main-service/src/main/java/ru/practicum/main_service/MainCommonUtils.java @@ -15,6 +15,6 @@ public class MainCommonUtils { public static final int MAX_LENGTH_TITLE = 120; public static final int MIN_LENGTH_UPDATE_TITLE = 3; public static final int MAX_LENGTH_UPDATE_TITLE = 50; -// public static final int MIN_LENGTH_COMMENT = 3; -// public static final int MAX_LENGTH_COMMENT = 7000; + public static final int MIN_LENGTH_COMMENT = 3; + public static final int MAX_LENGTH_COMMENT = 7000; } diff --git a/main-service/src/main/java/ru/practicum/main_service/comment/controller/CommentAdminController.java b/main-service/src/main/java/ru/practicum/main_service/comment/controller/CommentAdminController.java new file mode 100644 index 0000000..e6af61f --- /dev/null +++ b/main-service/src/main/java/ru/practicum/main_service/comment/controller/CommentAdminController.java @@ -0,0 +1,36 @@ +package ru.practicum.main_service.comment.controller; + +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.PageRequest; +import org.springframework.http.HttpStatus; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import ru.practicum.main_service.MainCommonUtils; +import ru.practicum.main_service.comment.dto.CommentDto; +import ru.practicum.main_service.comment.service.CommentService; + +import javax.validation.constraints.Positive; +import javax.validation.constraints.PositiveOrZero; +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/admin/comments") +@Validated +public class CommentAdminController { + private final CommentService commentService; + + @GetMapping + @ResponseStatus(HttpStatus.OK) + public List getCommentsByAdmin( + @RequestParam(required = false, defaultValue = MainCommonUtils.PAGE_DEFAULT_FROM) @PositiveOrZero Integer from, + @RequestParam(required = false, defaultValue = MainCommonUtils.PAGE_DEFAULT_SIZE) @Positive Integer size) { + return commentService.getCommentsByAdmin(PageRequest.of(from / size, size)); + } + + @DeleteMapping("/{commentId}") + @ResponseStatus(HttpStatus.NO_CONTENT) + public void deleteByAdmin(@PathVariable Long commentId) { + commentService.deleteByAdmin(commentId); + } +} diff --git a/main-service/src/main/java/ru/practicum/main_service/comment/controller/CommentPrivateController.java b/main-service/src/main/java/ru/practicum/main_service/comment/controller/CommentPrivateController.java new file mode 100644 index 0000000..f453c5f --- /dev/null +++ b/main-service/src/main/java/ru/practicum/main_service/comment/controller/CommentPrivateController.java @@ -0,0 +1,57 @@ +package ru.practicum.main_service.comment.controller; + +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.PageRequest; +import org.springframework.http.HttpStatus; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import ru.practicum.main_service.MainCommonUtils; +import ru.practicum.main_service.comment.dto.CommentDto; +import ru.practicum.main_service.comment.dto.NewCommentDto; +import ru.practicum.main_service.comment.service.CommentService; + +import javax.validation.Valid; +import javax.validation.constraints.Positive; +import javax.validation.constraints.PositiveOrZero; +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/users/{userId}/comments") +@Validated +public class CommentPrivateController { + private final CommentService commentService; + + @PostMapping + @ResponseStatus(HttpStatus.CREATED) + public CommentDto createByPrivate(@PathVariable Long userId, + @RequestParam Long eventId, + @Valid @RequestBody NewCommentDto newCommentDto) { + return commentService.createByPrivate(userId, eventId, newCommentDto); + } + + @PatchMapping("/{commentId}") + @ResponseStatus(HttpStatus.OK) + public CommentDto patchByPrivate(@PathVariable Long userId, + @PathVariable Long commentId, + @Valid @RequestBody NewCommentDto newCommentDto) { + return commentService.patchByPrivate(userId, commentId, newCommentDto); + } + + @DeleteMapping("/{commentId}") + @ResponseStatus(HttpStatus.NO_CONTENT) + public void deleteByPrivate(@PathVariable Long userId, + @PathVariable Long commentId) { + commentService.deleteByPrivate(userId, commentId); + } + + @GetMapping + @ResponseStatus(HttpStatus.OK) + public List getCommentsByPrivate( + @PathVariable Long userId, + @RequestParam(required = false) Long eventId, + @RequestParam(required = false, defaultValue = MainCommonUtils.PAGE_DEFAULT_FROM) @PositiveOrZero Integer from, + @RequestParam(required = false, defaultValue = MainCommonUtils.PAGE_DEFAULT_SIZE) @Positive Integer size) { + return commentService.getCommentsByPrivate(userId, eventId, PageRequest.of(from / size, size)); + } +} diff --git a/main-service/src/main/java/ru/practicum/main_service/comment/controller/CommentPublicController.java b/main-service/src/main/java/ru/practicum/main_service/comment/controller/CommentPublicController.java new file mode 100644 index 0000000..65b647e --- /dev/null +++ b/main-service/src/main/java/ru/practicum/main_service/comment/controller/CommentPublicController.java @@ -0,0 +1,37 @@ +package ru.practicum.main_service.comment.controller; + +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.PageRequest; +import org.springframework.http.HttpStatus; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import ru.practicum.main_service.MainCommonUtils; +import ru.practicum.main_service.comment.dto.CommentDto; +import ru.practicum.main_service.comment.service.CommentService; + +import javax.validation.constraints.Positive; +import javax.validation.constraints.PositiveOrZero; +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/comments") +@Validated +public class CommentPublicController { + private final CommentService commentService; + + @GetMapping + @ResponseStatus(HttpStatus.OK) + public List getCommentsByPublic( + @RequestParam Long eventId, + @RequestParam(required = false, defaultValue = MainCommonUtils.PAGE_DEFAULT_FROM) @PositiveOrZero Integer from, + @RequestParam(required = false, defaultValue = MainCommonUtils.PAGE_DEFAULT_SIZE) @Positive Integer size) { + return commentService.getCommentsByPublic(eventId, PageRequest.of(from / size, size)); + } + + @GetMapping("/{commentId}") + @ResponseStatus(HttpStatus.OK) + public CommentDto getCommentByPublic(@PathVariable Long commentId) { + return commentService.getCommentByPublic(commentId); + } +} diff --git a/main-service/src/main/java/ru/practicum/main_service/comment/dto/CommentDto.java b/main-service/src/main/java/ru/practicum/main_service/comment/dto/CommentDto.java new file mode 100644 index 0000000..06cb4d4 --- /dev/null +++ b/main-service/src/main/java/ru/practicum/main_service/comment/dto/CommentDto.java @@ -0,0 +1,27 @@ +package ru.practicum.main_service.comment.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.*; +import lombok.experimental.FieldDefaults; +import ru.practicum.main_service.MainCommonUtils; +import ru.practicum.main_service.user.dto.UserShortDto; + +import java.time.LocalDateTime; + +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CommentDto { + private Long id; + private String text; + private UserShortDto author; + private Long eventId; + + @JsonFormat(pattern = MainCommonUtils.DT_FORMAT, shape = JsonFormat.Shape.STRING) + private LocalDateTime createdOn; + + @JsonFormat(pattern = MainCommonUtils.DT_FORMAT, shape = JsonFormat.Shape.STRING) + private LocalDateTime editedOn; +} diff --git a/main-service/src/main/java/ru/practicum/main_service/comment/dto/NewCommentDto.java b/main-service/src/main/java/ru/practicum/main_service/comment/dto/NewCommentDto.java new file mode 100644 index 0000000..c4a3ced --- /dev/null +++ b/main-service/src/main/java/ru/practicum/main_service/comment/dto/NewCommentDto.java @@ -0,0 +1,20 @@ +package ru.practicum.main_service.comment.dto; + +import lombok.*; +import lombok.experimental.FieldDefaults; +import ru.practicum.main_service.MainCommonUtils; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + +@Getter +@Setter +@Builder +@ToString +@NoArgsConstructor +@AllArgsConstructor +public class NewCommentDto { + @NotBlank + @Size(min = MainCommonUtils.MIN_LENGTH_COMMENT, max = MainCommonUtils.MAX_LENGTH_COMMENT) + private String text; +} diff --git a/main-service/src/main/java/ru/practicum/main_service/comment/mapper/CommentMapper.java b/main-service/src/main/java/ru/practicum/main_service/comment/mapper/CommentMapper.java new file mode 100644 index 0000000..4136c3e --- /dev/null +++ b/main-service/src/main/java/ru/practicum/main_service/comment/mapper/CommentMapper.java @@ -0,0 +1,13 @@ +package ru.practicum.main_service.comment.mapper; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import ru.practicum.main_service.comment.dto.CommentDto; +import ru.practicum.main_service.comment.model.Comment; +import ru.practicum.main_service.user.mapper.UserMapper; + +@Mapper(componentModel = "spring", uses = {UserMapper.class}) +public interface CommentMapper { + @Mapping(target = "eventId", expression = "java(comment.getEvent().getId())") + CommentDto toCommentDto(Comment comment); +} diff --git a/main-service/src/main/java/ru/practicum/main_service/comment/model/Comment.java b/main-service/src/main/java/ru/practicum/main_service/comment/model/Comment.java new file mode 100644 index 0000000..a3dc66f --- /dev/null +++ b/main-service/src/main/java/ru/practicum/main_service/comment/model/Comment.java @@ -0,0 +1,43 @@ +package ru.practicum.main_service.comment.model; + +import lombok.*; +import lombok.experimental.FieldDefaults; +import org.hibernate.annotations.OnDelete; +import org.hibernate.annotations.OnDeleteAction; +import ru.practicum.main_service.MainCommonUtils; +import ru.practicum.main_service.event.model.Event; +import ru.practicum.main_service.user.model.User; + +import javax.persistence.*; +import java.time.LocalDateTime; + +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "comments", schema = "public") +public class Comment { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(length = MainCommonUtils.MAX_LENGTH_COMMENT, nullable = false) + private String text; + + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "author_id", referencedColumnName = "id") + @OnDelete(action = OnDeleteAction.CASCADE) + private User author; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "event_id", referencedColumnName = "id") + @OnDelete(action = OnDeleteAction.CASCADE) + private Event event; + + @Column(nullable = false) + private LocalDateTime createdOn; + + private LocalDateTime editedOn; +} diff --git a/main-service/src/main/java/ru/practicum/main_service/comment/repository/CommentRepository.java b/main-service/src/main/java/ru/practicum/main_service/comment/repository/CommentRepository.java new file mode 100644 index 0000000..57fac3c --- /dev/null +++ b/main-service/src/main/java/ru/practicum/main_service/comment/repository/CommentRepository.java @@ -0,0 +1,15 @@ +package ru.practicum.main_service.comment.repository; + +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import ru.practicum.main_service.comment.model.Comment; + +import java.util.List; + +public interface CommentRepository extends JpaRepository { + List findAllByAuthorId(Long userId); + + List findAllByAuthorIdAndEventId(Long userId, Long eventId); + + List findAllByEventId(Long eventId, Pageable pageable); +} diff --git a/main-service/src/main/java/ru/practicum/main_service/comment/service/CommentService.java b/main-service/src/main/java/ru/practicum/main_service/comment/service/CommentService.java new file mode 100644 index 0000000..902066d --- /dev/null +++ b/main-service/src/main/java/ru/practicum/main_service/comment/service/CommentService.java @@ -0,0 +1,25 @@ +package ru.practicum.main_service.comment.service; + +import org.springframework.data.domain.Pageable; +import ru.practicum.main_service.comment.dto.CommentDto; +import ru.practicum.main_service.comment.dto.NewCommentDto; + +import java.util.List; + +public interface CommentService { + List getCommentsByAdmin(Pageable pageable); + + void deleteByAdmin(Long commentId); + + List getCommentsByPrivate(Long userId, Long eventId, Pageable pageable); + + CommentDto createByPrivate(Long userId, Long eventId, NewCommentDto newCommentDto); + + CommentDto patchByPrivate(Long userId, Long commentId, NewCommentDto newCommentDto); + + void deleteByPrivate(Long userId, Long commentId); + + List getCommentsByPublic(Long eventId, Pageable pageable); + + CommentDto getCommentByPublic(Long commentId); +} diff --git a/main-service/src/main/java/ru/practicum/main_service/comment/service/CommentServiceImpl.java b/main-service/src/main/java/ru/practicum/main_service/comment/service/CommentServiceImpl.java new file mode 100644 index 0000000..90b2f8d --- /dev/null +++ b/main-service/src/main/java/ru/practicum/main_service/comment/service/CommentServiceImpl.java @@ -0,0 +1,154 @@ +package ru.practicum.main_service.comment.service; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import ru.practicum.main_service.comment.dto.CommentDto; +import ru.practicum.main_service.comment.dto.NewCommentDto; +import ru.practicum.main_service.comment.mapper.CommentMapper; +import ru.practicum.main_service.comment.model.Comment; +import ru.practicum.main_service.comment.repository.CommentRepository; +import ru.practicum.main_service.event.enums.EventState; +import ru.practicum.main_service.event.model.Event; +import ru.practicum.main_service.event.service.EventService; +import ru.practicum.main_service.exception.ForbiddenException; +import ru.practicum.main_service.exception.NotFoundException; +import ru.practicum.main_service.user.model.User; +import ru.practicum.main_service.user.service.UserService; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +@Slf4j +public class CommentServiceImpl implements CommentService { + private final UserService userService; + private final EventService eventService; + private final CommentRepository commentRepository; + private final CommentMapper commentMapper; + + @Override + public List getCommentsByAdmin(Pageable pageable) { + log.info("Вывод всех комментариев с пагинацией {}", pageable); + + return toCommentsDto(commentRepository.findAll(pageable).toList()); + } + + @Override + @Transactional + public void deleteByAdmin(Long commentId) { + log.info("Удаление комментария с id {}", commentId); + + commentRepository.deleteById(commentId); + } + + @Override + public List getCommentsByPrivate(Long userId, Long eventId, Pageable pageable) { + log.info("Вывод всех комментариев пользователя с id {} к событию с id {} и пагинацией {}", + userId, eventId, pageable); + + userService.getUserById(userId); + + List comments; + if (eventId != null) { + eventService.getEventById(eventId); + + comments = commentRepository.findAllByAuthorIdAndEventId(userId, eventId); + } else { + comments = commentRepository.findAllByAuthorId(userId); + } + + return toCommentsDto(comments); + } + + @Override + @Transactional + public CommentDto createByPrivate(Long userId, Long eventId, NewCommentDto newCommentDto) { + log.info("Создание комментария к событию с id {} пользователем с id {} и параметрами {}", + eventId, userId, newCommentDto); + + User user = userService.getUserById(userId); + Event event = eventService.getEventById(eventId); + + if (!event.getState().equals(EventState.PUBLISHED)) { + throw new ForbiddenException("Создавать комментарии можно только к опубликованным событиям."); + } + + Comment comment = Comment.builder() + .text(newCommentDto.getText()) + .author(user) + .event(event) + .createdOn(LocalDateTime.now()) + .build(); + + return commentMapper.toCommentDto(commentRepository.save(comment)); + } + + @Override + @Transactional + public CommentDto patchByPrivate(Long userId, Long commentId, NewCommentDto newCommentDto) { + log.info("Обновление комментария с id {} пользователем с id {} и параметрами {}", commentId, userId, newCommentDto); + + userService.getUserById(userId); + + Comment commentFromRepository = getCommentById(commentId); + + checkUserIsOwner(userId, commentFromRepository.getAuthor().getId()); + + commentFromRepository.setText(newCommentDto.getText()); + commentFromRepository.setEditedOn(LocalDateTime.now()); + + return commentMapper.toCommentDto(commentRepository.save(commentFromRepository)); + } + + @Override + @Transactional + public void deleteByPrivate(Long userId, Long commentId) { + log.info("Удаление комментария с id {} пользователем с id {}", commentId, userId); + + userService.getUserById(userId); + + checkUserIsOwner(userId, getCommentById(commentId).getAuthor().getId()); + + commentRepository.deleteById(commentId); + } + + @Override + public List getCommentsByPublic(Long eventId, Pageable pageable) { + log.info("Вывод всех комментариев к событию с id {} и пагинацией {}", eventId, pageable); + + eventService.getEventById(eventId); + + return toCommentsDto(commentRepository.findAllByEventId(eventId, pageable)); + } + + @Override + public CommentDto getCommentByPublic(Long commentId) { + log.info("Вывод комментария с id {}", commentId); + + return commentMapper.toCommentDto(getCommentById(commentId)); + } + + private List toCommentsDto(List comments) { + return comments.stream() + .map(commentMapper::toCommentDto) + .collect(Collectors.toList()); + } + + private Comment getCommentById(Long commentId) { + return commentRepository.findById(commentId) + .orElseThrow(() -> new NotFoundException("Комментария с таким id не существует.")); + } + + private void checkUserIsOwner(Long id, Long userId) { + if (!Objects.equals(id, userId)) { + throw new ForbiddenException("Пользователь не является владельцем."); + } + } +} diff --git a/main-service/src/main/resources/schema.sql b/main-service/src/main/resources/schema.sql index 02d8628..882ee18 100644 --- a/main-service/src/main/resources/schema.sql +++ b/main-service/src/main/resources/schema.sql @@ -59,3 +59,12 @@ CREATE TABLE IF NOT EXISTS compilations_events event_id BIGINT REFERENCES events (id) ON DELETE CASCADE ); +CREATE TABLE IF NOT EXISTS comments +( + id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + created_on TIMESTAMP NOT NULL, + edited_on TIMESTAMP, + text VARCHAR(7000) NOT NULL, + author_id BIGINT REFERENCES users (id) ON DELETE CASCADE, + event_id BIGINT REFERENCES events (id) ON DELETE CASCADE + ); \ No newline at end of file From 667c51e54767446ff5de22148bc09d98f61df338 Mon Sep 17 00:00:00 2001 From: Ilusha92 Date: Fri, 2 Jun 2023 15:49:12 +0300 Subject: [PATCH 2/6] Create feature.json --- Postman/feature.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Postman/feature.json b/Postman/feature.json index 403637b..8c2d0dd 100644 --- a/Postman/feature.json +++ b/Postman/feature.json @@ -1,7 +1,7 @@ { "info": { - "_postman_id": "506d43c6-b3fd-4fad-bd54-d5d902ce7a8a", - "name": "3 Feature - Comments", + "_postman_id": "a7a79a76-d87d-43a6-8553-91051991a2db", + "name": "Feature сomments", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", "_exporter_id": "24124777" }, From 5b74edfa2846ccf1b9f0295abaebdfa94b01837d Mon Sep 17 00:00:00 2001 From: Ilusha92 Date: Fri, 2 Jun 2023 15:51:22 +0300 Subject: [PATCH 3/6] codeStyle --- .../java/ru/practicum/main_service/comment/dto/CommentDto.java | 1 - .../ru/practicum/main_service/comment/dto/NewCommentDto.java | 1 - .../java/ru/practicum/main_service/comment/model/Comment.java | 1 - 3 files changed, 3 deletions(-) diff --git a/main-service/src/main/java/ru/practicum/main_service/comment/dto/CommentDto.java b/main-service/src/main/java/ru/practicum/main_service/comment/dto/CommentDto.java index 06cb4d4..8b7bfc6 100644 --- a/main-service/src/main/java/ru/practicum/main_service/comment/dto/CommentDto.java +++ b/main-service/src/main/java/ru/practicum/main_service/comment/dto/CommentDto.java @@ -2,7 +2,6 @@ import com.fasterxml.jackson.annotation.JsonFormat; import lombok.*; -import lombok.experimental.FieldDefaults; import ru.practicum.main_service.MainCommonUtils; import ru.practicum.main_service.user.dto.UserShortDto; diff --git a/main-service/src/main/java/ru/practicum/main_service/comment/dto/NewCommentDto.java b/main-service/src/main/java/ru/practicum/main_service/comment/dto/NewCommentDto.java index c4a3ced..8716e37 100644 --- a/main-service/src/main/java/ru/practicum/main_service/comment/dto/NewCommentDto.java +++ b/main-service/src/main/java/ru/practicum/main_service/comment/dto/NewCommentDto.java @@ -1,7 +1,6 @@ package ru.practicum.main_service.comment.dto; import lombok.*; -import lombok.experimental.FieldDefaults; import ru.practicum.main_service.MainCommonUtils; import javax.validation.constraints.NotBlank; diff --git a/main-service/src/main/java/ru/practicum/main_service/comment/model/Comment.java b/main-service/src/main/java/ru/practicum/main_service/comment/model/Comment.java index a3dc66f..1b84bf1 100644 --- a/main-service/src/main/java/ru/practicum/main_service/comment/model/Comment.java +++ b/main-service/src/main/java/ru/practicum/main_service/comment/model/Comment.java @@ -1,7 +1,6 @@ package ru.practicum.main_service.comment.model; import lombok.*; -import lombok.experimental.FieldDefaults; import org.hibernate.annotations.OnDelete; import org.hibernate.annotations.OnDeleteAction; import ru.practicum.main_service.MainCommonUtils; From 9698dca5179f0f087378045490aa66ca051e6724 Mon Sep 17 00:00:00 2001 From: Ilusha92 Date: Fri, 2 Jun 2023 16:00:04 +0300 Subject: [PATCH 4/6] Update feature.json --- Postman/feature.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Postman/feature.json b/Postman/feature.json index 8c2d0dd..1bee806 100644 --- a/Postman/feature.json +++ b/Postman/feature.json @@ -1,7 +1,7 @@ { "info": { "_postman_id": "a7a79a76-d87d-43a6-8553-91051991a2db", - "name": "Feature сomments", + "name": "3 Feature - Comments", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", "_exporter_id": "24124777" }, From d0eb1fa5516247c29c089e87b83b5b35c59ae63a Mon Sep 17 00:00:00 2001 From: Ilusha92 Date: Fri, 2 Jun 2023 16:04:26 +0300 Subject: [PATCH 5/6] Update feature.json --- {Postman => postman}/feature.json | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {Postman => postman}/feature.json (100%) diff --git a/Postman/feature.json b/postman/feature.json similarity index 100% rename from Postman/feature.json rename to postman/feature.json From afbd2bbc613f44f7f5632bafcfc63ad0961e1ddd Mon Sep 17 00:00:00 2001 From: Ilusha92 Date: Fri, 2 Jun 2023 16:09:25 +0300 Subject: [PATCH 6/6] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 18a246e..850fe94 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,4 @@ # java-explore-with-me Template repository for ExploreWithMe project. + +https://github.com/Ilusha92/java-explore-with-me/pull/8 \ No newline at end of file