diff --git a/Postman/ewm-main-service.json b/Postman/ewm-main-service.json new file mode 100644 index 0000000..2ab4c3e --- /dev/null +++ b/Postman/ewm-main-service.json @@ -0,0 +1,14630 @@ +{ + "info": { + "_postman_id": "4f622f31-328a-4506-95bd-66359cfbe749", + "name": "Test Explore With Me - Main service", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "23073145", + "_collection_link": "https://universal-shadow-295426.postman.co/workspace/My-Workspace~4200f6aa-0504-44b1-8a1d-707d0dcbd5ce/collection/13708500-4f622f31-328a-4506-95bd-66359cfbe749?action=share&source=collection_link&creator=23073145" + }, + "item": [ + { + "name": "Validation", + "item": [ + { + "name": "Event", + "item": [ + { + "name": "Required query params", + "item": [ + { + "name": "Добавление запроса от текущего пользователя на участие в событии без обязательного query params", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " const submittedUser = await api.addUser(rnd.getUser());\r", + " pm.request.removeQueryParams(['eventId']);\r", + " pm.collectionVariables.set('uid', submittedUser.id);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/users/:userId/requests", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "requests" + ], + "query": [ + { + "key": "eventId", + "value": "0", + "description": "(Required) id события", + "disabled": true + } + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + } + ] + }, + "description": "Обратите внимание:\n- нельзя добавить повторный запрос\n- инициатор события не может добавить запрос на участие в своём событии\n- нельзя участвовать в неопубликованном событии\n- если у события достигнут лимит запросов на участие - необходимо вернуть ошибку\n- если для события отключена пре-модерация запросов на участие, то запрос должен автоматически перейти в состояние подтвержденного" + }, + "response": [] + } + ] + }, + { + "name": "Unrequired query params", + "item": [ + { + "name": "Получение событий, добавленных текущим пользователем без нескольких Query params", + "event": [ + { + "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", + " const event = await api.addEvent(user.id, rnd.getEvent(category.id));\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", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/users/:userId/events", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events" + ], + "query": [ + { + "key": "from", + "value": "0", + "description": "количество элементов, которые нужно пропустить для формирования текущего набора", + "disabled": true + }, + { + "key": "size", + "value": "1000", + "description": "количество элементов в наборе", + "disabled": true + } + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + } + ] + } + }, + "response": [] + }, + { + "name": "Получение событий с возможностью фильтрации без нескольких Query params", + "event": [ + { + "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", + " 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.request.removeQueryParams(['text', 'categories', 'paid']);\r", + " pm.request.addQueryParams([`text=` + event.annotation, 'categories=' + category.id, 'paid=' + event.paid]);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/events", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "events" + ], + "query": [ + { + "key": "text", + "value": "0", + "description": "текст для поиска в содержимом аннотации и подробном описании события", + "disabled": true + }, + { + "key": "categories", + "value": "0", + "description": "список идентификаторов категорий в которых будет вестись поиск", + "disabled": true + }, + { + "key": "paid", + "value": "true", + "description": "поиск только платных/бесплатных событий", + "disabled": true + }, + { + "key": "rangeStart", + "value": "2022-01-06%2013%3A30%3A38", + "description": "дата и время не раньше которых должно произойти событие", + "disabled": true + }, + { + "key": "rangeEnd", + "value": "2097-09-06%2013%3A30%3A38", + "description": "дата и время не позже которых должно произойти событие", + "disabled": true + }, + { + "key": "onlyAvailable", + "value": "false", + "description": "только события у которых не исчерпан лимит запросов на участие", + "disabled": true + }, + { + "key": "sort", + "value": "EVENT_DATE", + "description": "Вариант сортировки: по дате события или по количеству просмотров", + "disabled": true + }, + { + "key": "from", + "value": "0", + "description": "количество событий, которые нужно пропустить для формирования текущего набора", + "disabled": true + }, + { + "key": "size", + "value": "1000", + "description": "количество событий в наборе", + "disabled": true + } + ] + }, + "description": "Обратите внимание: \n- это публичный эндпоинт, соответственно в выдаче должны быть только опубликованные события\n- текстовый поиск (по аннотации и подробному описанию) должен быть без учета регистра букв\n- если в запросе не указан диапазон дат [rangeStart-rangeEnd], то нужно выгружать события, которые произойдут позже текущей даты и времени\n- информация о каждом событии должна включать в себя количество просмотров и количество уже одобренных заявок на участие\n- информацию о том, что по этому эндпоинту был осуществлен и обработан запрос, нужно сохранить в сервисе статистики" + }, + "response": [] + } + ] + }, + { + "name": "Required params in body", + "item": [ + { + "name": "Добавление события без поля description", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let event;\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", + " event = rnd.getEvent(category.id);\r", + " delete event[\"description\"];\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(event),\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" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/events", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + } + ] + }, + "description": "Обратите внимание: дата и время на которые намечено событие не может быть раньше, чем через два часа от текущего момента" + }, + "response": [] + }, + { + "name": "Добавление события с пустым описанием", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let event;\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", + " event = rnd.getEvent(category.id);\r", + " event[\"description\"] = '';\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(event),\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" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/events", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + } + ] + }, + "description": "Обратите внимание: дата и время на которые намечено событие не может быть раньше, чем через два часа от текущего момента" + }, + "response": [] + }, + { + "name": "Добавление события со строкой из пробелов в качестве описания", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let event;\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", + " event = rnd.getEvent(category.id);\r", + " event[\"description\"] = ' ';\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(event),\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" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/events", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + } + ] + }, + "description": "Обратите внимание: дата и время на которые намечено событие не может быть раньше, чем через два часа от текущего момента" + }, + "response": [] + }, + { + "name": "Добавление события без поля annotation", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let event;\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", + " event = rnd.getEvent(category.id);\r", + " delete event[\"annotation\"];\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(event),\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" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/events", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + } + ] + }, + "description": "Обратите внимание: дата и время на которые намечено событие не может быть раньше, чем через два часа от текущего момента" + }, + "response": [] + }, + { + "name": "Добавление события с пустой аннотацией", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let event;\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", + " event = rnd.getEvent(category.id);\r", + " event[\"annotation\"] = '';\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(event),\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" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/events", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + } + ] + }, + "description": "Обратите внимание: дата и время на которые намечено событие не может быть раньше, чем через два часа от текущего момента" + }, + "response": [] + }, + { + "name": "Добавление события со строкой из пробелов в качестве аннотации", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let event;\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", + " event = rnd.getEvent(category.id);\r", + " event[\"annotation\"] = ' ';\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(event),\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" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/events", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + } + ] + }, + "description": "Обратите внимание: дата и время на которые намечено событие не может быть раньше, чем через два часа от текущего момента" + }, + "response": [] + }, + { + "name": "Добавление события с отрицательным лимитом участников", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let event;\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", + " event = rnd.getEvent(category.id);\r", + " event[\"participantLimit\"] = -6;\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(event),\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" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/events", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + } + ] + }, + "description": "Обратите внимание: дата и время на которые намечено событие не может быть раньше, чем через два часа от текущего момента" + }, + "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", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " const event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " pm.collectionVariables.set(\"uid\", user.id);\r", + " pm.collectionVariables.set(\"eid\", event.id);\r", + " pm.collectionVariables.set(\"response\", event);\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({\r", + " participantLimit : -156\r", + " }),\r", + " options: { raw: { language: 'json' } }\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(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.have.status(400);\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/events/:eventId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events", + ":eventId" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + }, + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id отменяемого события" + } + ] + }, + "description": "Обратите внимание: Отменить можно только событие в состоянии ожидания модерации." + }, + "response": [] + } + ] + }, + { + "name": "Misc tests", + "item": [ + { + "name": "Отклонение публикации события", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let new_event, event;\r", + " try {\r", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " await pm.sendRequest({\r", + " url : \"http://localhost:8080/admin/events/\" + event.id,\r", + " method : \"PATCH\",\r", + " header: { \"Content-Type\": \"application/json\" },\r", + " body: JSON.stringify({\r", + " stateAction: \"REJECT_EVENT\"\r", + " })\r", + " }, (error, response) => {\r", + "\r", + " });\r", + " pm.collectionVariables.set(\"uid\", user.id);\r", + " pm.collectionVariables.set(\"eid\", event.id);\r", + " pm.collectionVariables.set(\"response\", event);\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({\r", + " stateAction: \"SEND_TO_REVIEW\"\r", + " }),\r", + " options: { raw: { language: 'json' } }\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 source = pm.collectionVariables.get(\"response\");\r", + "const target = pm.response.json();\r", + "\r", + "\r", + "pm.test(\"Событие должно содержать поля: id, title, annotation, category, paid, eventDate, initiator, description, participantLimit, state, createdOn, location, requestModeration\", function () {\r", + "pm.expect(target).to.have.property('id');\r", + "pm.expect(target).to.have.property('title');\r", + "pm.expect(target).to.have.property('annotation');\r", + "pm.expect(target).to.have.property('category');\r", + "pm.expect(target).to.have.property('paid');\r", + "pm.expect(target).to.have.property('eventDate');\r", + "pm.expect(target).to.have.property('initiator');\r", + "pm.expect(target).to.have.property('description');\r", + "pm.expect(target).to.have.property('participantLimit');\r", + "pm.expect(target).to.have.property('state');\r", + "pm.expect(target).to.have.property('createdOn');\r", + "pm.expect(target).to.have.property('location');\r", + "pm.expect(target).to.have.property('requestModeration');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(source.annotation).equal(target.annotation, 'Аннотация отменённого события должна соответствовать аннотации события до отмены');\r", + " pm.expect(source.category.id).equal(target.category.id, 'Категория отменённого события должна соответствовать категории события до отмены');\r", + " pm.expect(source.paid.toString()).equal(target.paid.toString(), 'Стоимость отменённого события должна соответствовать стоимости события до отмены');\r", + " pm.expect(source.eventDate).equal(target.eventDate, 'Дата проведения отменённого события должна соответствовать дате проведения события до отмены');\r", + " pm.expect(source.description).equal(target.description, 'Описание отменённого события должно соответствовать описанию события до отмены');\r", + " pm.expect(source.title).equal(target.title, 'Название отменённого события должно соответствовать названию события до отмены');\r", + " pm.expect(source.participantLimit.toString()).equal(target.participantLimit.toString(), 'Лимит участников отменённого события должен соответствовать лимиту участников события до отмены');\r", + " pm.expect(source.paid.toString()).equal(target.paid.toString(), 'Стоимость отменённого события должна соответствовать стоимости события до отмены');\r", + "});\r", + "\r", + "pm.test(\"Событие должно иметь статус CANCELED при возвращении от администратора и статус PENDING после выполнения запроса\", function () {\r", + " pm.expect(target.state).equal(\"PENDING\");\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/events/:eventId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events", + ":eventId" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + }, + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id отменяемого события" + } + ] + }, + "description": "Обратите внимание: Отменить можно только событие в состоянии ожидания модерации." + }, + "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", + " const user = await api.addUser(rnd.getUser());\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", + " pm.collectionVariables.set('response', event);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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(\"Ответ должен содержать код статуса 404 и данные в формате json\", function () {\r", + " pm.response.to.be.notFound; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/events/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "events", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "{{eid}}", + "description": "(Required) id события" + } + ] + }, + "description": "Обратите внимание:\n- событие должно быть опубликовано\n- информация о событии должна включать в себя количество просмотров и количество подтвержденных запросов\n- информацию о том, что по этому эндпоинту был осуществлен и обработан запрос, нужно сохранить в сервисе статистики" + }, + "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", + " const user = await api.addUser(rnd.getUser());\r", + " const user2 = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " let event = rnd.getEvent(category.id)\r", + " event.requestModeration = true;\r", + " event.participantLimit = 2;\r", + " event = await api.addEvent(user.id, event);\r", + " event = await api.publishEvent(event.id);\r", + " pm.request.removeQueryParams(['users', 'categories']);\r", + " pm.request.addQueryParams([`users=` + user.id, 'categories=' + category.id]);\r", + " pm.collectionVariables.set('response', event);\r", + " await pm.sendRequest({\r", + " url : \"http://localhost:8080/admin/events?users=\" + user.id +\"&states=PUBLISHED&categories=\" + category.id + \"&rangeStart=2022-01-06%2013%3A30%3A38&rangeEnd=2097-09-06%2013%3A30%3A38&from=0&size=1000\",\r", + " method : \"GET\",\r", + " header: { \"Content-Type\": \"application/json\" }\r", + " }, (error, response) => {\r", + " pm.collectionVariables.set('confirmedRequests', response.json()[0].confirmedRequests)\r", + " });\r", + " const requestToJoin = await api.publishParticipationRequest(event.id, user2.id);\r", + " const confirmedRequest = await api.acceptParticipationRequest(event.id, user.id, requestToJoin.id);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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 source = pm.collectionVariables.get('response');\r", + "const target = pm.response.json()[0];\r", + "\r", + "pm.test(\"Событие должно содержать поля: id, title, annotation, category, paid, eventDate, initiator, views, confirmedRequests, description, participantLimit, state, createdOn, publishedOn, location, requestModeration\", function () {\r", + "pm.expect(target).to.have.property('id');\r", + "pm.expect(target).to.have.property('title');\r", + "pm.expect(target).to.have.property('annotation');\r", + "pm.expect(target).to.have.property('category');\r", + "pm.expect(target).to.have.property('paid');\r", + "pm.expect(target).to.have.property('eventDate');\r", + "pm.expect(target).to.have.property('initiator');\r", + "pm.expect(target).to.have.property('views');\r", + "pm.expect(target).to.have.property('confirmedRequests');\r", + "pm.expect(target).to.have.property('description');\r", + "pm.expect(target).to.have.property('participantLimit');\r", + "pm.expect(target).to.have.property('state');\r", + "pm.expect(target).to.have.property('createdOn');\r", + "pm.expect(target).to.have.property('publishedOn');\r", + "pm.expect(target).to.have.property('location');\r", + "pm.expect(target).to.have.property('requestModeration');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(source.annotation).equal(target.annotation, 'Аннотация события должна соответствовать искомому событию');\r", + " pm.expect(source.category.id).equal(target.category.id, 'Идентификатор категории должен соответствовать искомой категории');\r", + " pm.expect(source.paid.toString()).equal(target.paid.toString(), 'Стоимость посещения события должна соответствовать искомому событию');\r", + " pm.expect(source.eventDate).equal(target.eventDate, 'Дата проведения события должна соответствовать дате искомого события');\r", + " pm.expect(source.description).equal(target.description, 'Описание события должно соответствовать искомому событию');\r", + " pm.expect(source.title).equal(target.title, 'Название события должно соответствовать искомому событию');\r", + " pm.expect(source.participantLimit.toString()).equal(target.participantLimit.toString(), 'Число участников события должно соответствовать искомому событию');\r", + " pm.expect(pm.collectionVariables.get('confirmedRequests')).equal(0);\r", + " pm.expect(target.confirmedRequests).equal(1);\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/admin/events?users=0&states=PUBLISHED&categories=0&rangeStart=2022-01-06%2013%3A30%3A38&rangeEnd=2097-09-06%2013%3A30%3A38&from=0&size=1000", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "events" + ], + "query": [ + { + "key": "users", + "value": "0", + "description": "список id пользователей, чьи события нужно найти" + }, + { + "key": "states", + "value": "PUBLISHED", + "description": "список состояний в которых находятся искомые события" + }, + { + "key": "categories", + "value": "0", + "description": "список id категорий в которых будет вестись поиск" + }, + { + "key": "rangeStart", + "value": "2022-01-06%2013%3A30%3A38", + "description": "дата и время не раньше которых должно произойти событие" + }, + { + "key": "rangeEnd", + "value": "2097-09-06%2013%3A30%3A38", + "description": "дата и время не позже которых должно произойти событие" + }, + { + "key": "from", + "value": "0", + "description": "количество событий, которые нужно пропустить для формирования текущего набора" + }, + { + "key": "size", + "value": "1000", + "description": "количество событий в наборе" + } + ] + }, + "description": "Эндпоинт возвращает полную информацию обо всех событиях подходящих под переданные условия" + }, + "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", + " \r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/events?text=0&categories=0&paid=true&rangeStart=2022-01-06%2013%3A30%3A38&rangeEnd=2007-09-06%2013%3A30%3A38&onlyAvailable=false&sort=EVENT_DATE&from=0&size=1000", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "events" + ], + "query": [ + { + "key": "text", + "value": "0", + "description": "текст для поиска в содержимом аннотации и подробном описании события" + }, + { + "key": "categories", + "value": "0", + "description": "список идентификаторов категорий в которых будет вестись поиск" + }, + { + "key": "paid", + "value": "true", + "description": "поиск только платных/бесплатных событий" + }, + { + "key": "rangeStart", + "value": "2022-01-06%2013%3A30%3A38", + "description": "дата и время не раньше которых должно произойти событие" + }, + { + "key": "rangeEnd", + "value": "2007-09-06%2013%3A30%3A38", + "description": "дата и время не позже которых должно произойти событие" + }, + { + "key": "onlyAvailable", + "value": "false", + "description": "только события у которых не исчерпан лимит запросов на участие" + }, + { + "key": "sort", + "value": "EVENT_DATE", + "description": "Вариант сортировки: по дате события или по количеству просмотров" + }, + { + "key": "from", + "value": "0", + "description": "количество событий, которые нужно пропустить для формирования текущего набора" + }, + { + "key": "size", + "value": "1000", + "description": "количество событий в наборе" + } + ] + }, + "description": "Обратите внимание: \n- это публичный эндпоинт, соответственно в выдаче должны быть только опубликованные события\n- текстовый поиск (по аннотации и подробному описанию) должен быть без учета регистра букв\n- если в запросе не указан диапазон дат [rangeStart-rangeEnd], то нужно выгружать события, которые произойдут позже текущей даты и времени\n- информация о каждом событии должна включать в себя количество просмотров и количество уже одобренных заявок на участие\n- информацию о том, что по этому эндпоинту был осуществлен и обработан запрос, нужно сохранить в сервисе статистики" + }, + "response": [] + }, + { + "name": "Проверка работы поля views", + "event": [ + { + "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", + " 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", + " await api.findEvent(event.id)\r", + " await api.findEvent(event.id)\r", + " pm.collectionVariables.set(\"eid\", event.id)\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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", + "\r", + "const target = pm.response.json();\r", + "\r", + "pm.test(\"Событие должно содержать поля: id, title, annotation, category, paid, eventDate, initiator, views, confirmedRequests, description, participantLimit, state, createdOn, publishedOn, location, requestModeration\", function () {\r", + "pm.expect(target).to.have.property('id');\r", + "pm.expect(target).to.have.property('title');\r", + "pm.expect(target).to.have.property('annotation');\r", + "pm.expect(target).to.have.property('category');\r", + "pm.expect(target).to.have.property('paid');\r", + "pm.expect(target).to.have.property('eventDate');\r", + "pm.expect(target).to.have.property('initiator');\r", + "pm.expect(target).to.have.property('views');\r", + "pm.expect(target).to.have.property('confirmedRequests');\r", + "pm.expect(target).to.have.property('description');\r", + "pm.expect(target).to.have.property('participantLimit');\r", + "pm.expect(target).to.have.property('state');\r", + "pm.expect(target).to.have.property('createdOn');\r", + "pm.expect(target).to.have.property('publishedOn');\r", + "pm.expect(target).to.have.property('location');\r", + "pm.expect(target).to.have.property('requestModeration');\r", + "});\r", + "\r", + "pm.test(\"Значение поля views должно увеличится на 1 после выполнения GET запроса с уникального IP к событию\", function () {\r", + " pm.expect(target.views).equal(1);\r", + " \r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/events/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "events", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "{{eid}}", + "description": "(Required) id события" + } + ] + }, + "description": "Обратите внимание:\n- событие должно быть опубликовано\n- информация о событии должна включать в себя количество просмотров и количество подтвержденных запросов\n- информацию о том, что по этому эндпоинту был осуществлен и обработан запрос, нужно сохранить в сервисе статистики" + }, + "response": [] + }, + { + "name": "Добавление запроса на участие при participantLimit == 0", + "event": [ + { + "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", + " const category = await api.addCategory(rnd.getCategory());\r", + " let eventBody = rnd.getEvent(category.id);\r", + " eventBody['requestModeration'] = true\r", + " eventBody['participantLimit'] = 0\r", + " let event = await api.addEvent(user.id, eventBody);\r", + " event = await api.publishEvent(event.id);\r", + " const submittedUser = await api.addUser(rnd.getUser());\r", + " pm.request.removeQueryParams(['eventId']);\r", + " pm.request.addQueryParams([`eventId=` + event.id]);\r", + " pm.collectionVariables.set('uid', submittedUser.id);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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(\"Ответ должен содержать код статуса 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 target = pm.response.json();\r", + "var query = {};\r", + "pm.request.url.query.all().forEach((param) => { query[param.key] = param.value});\r", + "\r", + "pm.test(\"Запрос на участие должен содержать поля: id, requester, event, status, created\", function () {\r", + "pm.expect(target).to.have.property('id');\r", + "pm.expect(target).to.have.property('requester');\r", + "pm.expect(target).to.have.property('event');\r", + "pm.expect(target).to.have.property('status');\r", + "pm.expect(target).to.have.property('created');\r", + "});\r", + "\r", + "pm.test(\"При создании у запроса на участие должен быть статус CONFIRMED\", function () {\r", + " pm.expect(target.status).equal(\"CONFIRMED\");\r", + "});\r", + "\r", + "pm.test(\"Id ивента в запросе и в ответе должны совпадать\", function () {\r", + " pm.expect(target.event.toString()).equal(query['eventId'].toString());\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/users/:userId/requests?eventId=0", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "requests" + ], + "query": [ + { + "key": "eventId", + "value": "0", + "description": "(Required) id события" + } + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + } + ] + }, + "description": "Обратите внимание:\n- нельзя добавить повторный запрос\n- инициатор события не может добавить запрос на участие в своём событии\n- нельзя участвовать в неопубликованном событии\n- если у события достигнут лимит запросов на участие - необходимо вернуть ошибку\n- если для события отключена пре-модерация запросов на участие, то запрос должен автоматически перейти в состояние подтвержденного" + }, + "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", + " const user = await api.addUser(rnd.getUser());\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", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({\r", + " eventDate : \"2020-10-11 23:10:05\"\r", + " }),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.have.status(400);\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}" + }, + "url": { + "raw": "{{baseUrl}}/admin/events/:eventId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "events", + ":eventId" + ], + "variable": [ + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id события" + } + ] + }, + "description": "Обратите внимание:\n - дата начала события должна быть не ранее чем за час от даты публикации.\n- событие должно быть в состоянии ожидания публикации" + }, + "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", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " const event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " pm.collectionVariables.set(\"uid\", user.id);\r", + " pm.collectionVariables.set(\"eid\", event.id);\r", + " pm.collectionVariables.set(\"response\", event);\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({\r", + " eventDate : \"2020-10-11 23:10:05\"\r", + " }),\r", + " options: { raw: { language: 'json' } }\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(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.have.status(400);\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/events/:eventId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events", + ":eventId" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + }, + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id отменяемого события" + } + ] + }, + "description": "Обратите внимание: Отменить можно только событие в состоянии ожидания модерации." + }, + "response": [] + }, + { + "name": "Добавление события на неподходящую дату", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let event;\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", + " event = rnd.getEvent(category.id);\r", + " event.eventDate = \"2020-12-31 15:10:05\";\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(event),\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" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.have.status(400);\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/events", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + } + ] + }, + "description": "Обратите внимание: дата и время на которые намечено событие не может быть раньше, чем через два часа от текущего момента" + }, + "response": [] + } + ] + }, + { + "name": "String length restrictions", + "item": [ + { + "name": "Добавление нового события с description.length < 20", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let event;\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", + " event = rnd.getEvent(category.id);\r", + " event.description = rnd.getWord(19);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(event),\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" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/events", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + } + ] + }, + "description": "Обратите внимание: дата и время на которые намечено событие не может быть раньше, чем через два часа от текущего момента" + }, + "response": [] + }, + { + "name": "Добавление нового события с description.length < 20", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let event;\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", + " event = rnd.getEvent(category.id);\r", + " event.description = rnd.getWord(19);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(event),\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" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/events", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + } + ] + }, + "description": "Обратите внимание: дата и время на которые намечено событие не может быть раньше, чем через два часа от текущего момента" + }, + "response": [] + }, + { + "name": "Добавление нового события с annotation.length < 20", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let event;\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", + " event = rnd.getEvent(category.id);\r", + " event.annotation = rnd.getWord(19);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(event),\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" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/events", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + } + ] + }, + "description": "Обратите внимание: дата и время на которые намечено событие не может быть раньше, чем через два часа от текущего момента" + }, + "response": [] + }, + { + "name": "Добавление нового события с annotation.length > 2000", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let event;\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", + " event = rnd.getEvent(category.id);\r", + " event.annotation = rnd.getWord(2001);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(event),\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" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/events", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + } + ] + }, + "description": "Обратите внимание: дата и время на которые намечено событие не может быть раньше, чем через два часа от текущего момента" + }, + "response": [] + }, + { + "name": "Добавление нового события с description.length == 20 && annotation.length == 20 && title.length == 3", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let event;\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", + " event = rnd.getEvent(category.id);\r", + " event.description = rnd.getWord(20);\r", + " event.annotation = rnd.getWord(20);\r", + " event.title = rnd.getWord(3);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(event),\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" + } + }, + { + "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", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/events", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + } + ] + }, + "description": "Обратите внимание: дата и время на которые намечено событие не может быть раньше, чем через два часа от текущего момента" + }, + "response": [] + }, + { + "name": "Добавление нового события с description.length == 7000 && annotation.length == 2000 && title.length == 120", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let event;\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", + " event = rnd.getEvent(category.id);\r", + " event.description = rnd.getWord(7000);\r", + " event.annotation = rnd.getWord(2000);\r", + " event.title = rnd.getWord(120);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(event),\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" + } + }, + { + "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", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/events", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + } + ] + }, + "description": "Обратите внимание: дата и время на которые намечено событие не может быть раньше, чем через два часа от текущего момента" + }, + "response": [] + }, + { + "name": "Добавление нового события с title.length < 3", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let event;\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", + " event = rnd.getEvent(category.id);\r", + " event.title = rnd.getWord(2);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(event),\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" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/events", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + } + ] + }, + "description": "Обратите внимание: дата и время на которые намечено событие не может быть раньше, чем через два часа от текущего момента" + }, + "response": [] + }, + { + "name": "Добавление нового события с title.length > 120", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let event;\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", + " event = rnd.getEvent(category.id);\r", + " event.title = rnd.getWord(121);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(event),\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" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/events", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + } + ] + }, + "description": "Обратите внимание: дата и время на которые намечено событие не может быть раньше, чем через два часа от текущего момента" + }, + "response": [] + }, + { + "name": "Изменение заголовка события с title.length < 3 (admin endpoint)", + "event": [ + { + "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", + " const category = await api.addCategory(rnd.getCategory());\r", + " let event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " let event2 = rnd.getEvent(category.id)\r", + " event2.title = rnd.getWord(2);\r", + " pm.collectionVariables.set(\"eid\", event.id)\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: event2,\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}" + }, + "url": { + "raw": "{{baseUrl}}/admin/events/:eventId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "events", + ":eventId" + ], + "variable": [ + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id события" + } + ] + }, + "description": "Обратите внимание:\n - дата начала события должна быть не ранее чем за час от даты публикации.\n- событие должно быть в состоянии ожидания публикации" + }, + "response": [] + }, + { + "name": "Изменение заголовка события с title.length > 120 (admin endpoint)", + "event": [ + { + "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", + " const category = await api.addCategory(rnd.getCategory());\r", + " let event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " let event2 = rnd.getEvent(category.id)\r", + " event2.title = rnd.getWord(121);\r", + " pm.collectionVariables.set(\"eid\", event.id)\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: event2,\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}" + }, + "url": { + "raw": "{{baseUrl}}/admin/events/:eventId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "events", + ":eventId" + ], + "variable": [ + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id события" + } + ] + }, + "description": "Обратите внимание:\n - дата начала события должна быть не ранее чем за час от даты публикации.\n- событие должно быть в состоянии ожидания публикации" + }, + "response": [] + }, + { + "name": "Изменение описания события с description.length < 20 (admin endpoint)", + "event": [ + { + "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", + " const category = await api.addCategory(rnd.getCategory());\r", + " let event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " let event2 = rnd.getEvent(category.id)\r", + " event2.description = rnd.getWord(19);\r", + " pm.collectionVariables.set(\"eid\", event.id)\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: event2,\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}" + }, + "url": { + "raw": "{{baseUrl}}/admin/events/:eventId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "events", + ":eventId" + ], + "variable": [ + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id события" + } + ] + }, + "description": "Обратите внимание:\n - дата начала события должна быть не ранее чем за час от даты публикации.\n- событие должно быть в состоянии ожидания публикации" + }, + "response": [] + }, + { + "name": "Изменение описания события с description.length > 7000 (admin endpoint)", + "event": [ + { + "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", + " const category = await api.addCategory(rnd.getCategory());\r", + " let event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " let event2 = rnd.getEvent(category.id)\r", + " event2.description = rnd.getWord(7001);\r", + " pm.collectionVariables.set(\"eid\", event.id)\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: event2,\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}" + }, + "url": { + "raw": "{{baseUrl}}/admin/events/:eventId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "events", + ":eventId" + ], + "variable": [ + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id события" + } + ] + }, + "description": "Обратите внимание:\n - дата начала события должна быть не ранее чем за час от даты публикации.\n- событие должно быть в состоянии ожидания публикации" + }, + "response": [] + }, + { + "name": "Изменение аннотации события с annotation.length < 20 (admin endpoint)", + "event": [ + { + "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", + " const category = await api.addCategory(rnd.getCategory());\r", + " let event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " let event2 = rnd.getEvent(category.id)\r", + " event2.annotation = rnd.getWord(19);\r", + " pm.collectionVariables.set(\"eid\", event.id)\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: event2,\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}" + }, + "url": { + "raw": "{{baseUrl}}/admin/events/:eventId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "events", + ":eventId" + ], + "variable": [ + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id события" + } + ] + }, + "description": "Обратите внимание:\n - дата начала события должна быть не ранее чем за час от даты публикации.\n- событие должно быть в состоянии ожидания публикации" + }, + "response": [] + }, + { + "name": "Изменение аннотации события с annotation.length > 2000 (admin endpoint)", + "event": [ + { + "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", + " const category = await api.addCategory(rnd.getCategory());\r", + " let event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " let event2 = rnd.getEvent(category.id)\r", + " event2.annotation = rnd.getWord(2001);\r", + " pm.collectionVariables.set(\"eid\", event.id)\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: event2,\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}" + }, + "url": { + "raw": "{{baseUrl}}/admin/events/:eventId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "events", + ":eventId" + ], + "variable": [ + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id события" + } + ] + }, + "description": "Обратите внимание:\n - дата начала события должна быть не ранее чем за час от даты публикации.\n- событие должно быть в состоянии ожидания публикации" + }, + "response": [] + }, + { + "name": "Изменение события с description.length == 20 && annotation.length == 20 && title.length == 3 (admin endpoint)", + "event": [ + { + "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", + " const category = await api.addCategory(rnd.getCategory());\r", + " let event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " let event2 = rnd.getEvent(category.id)\r", + " event2.annotation = rnd.getWord(20);\r", + " event2.description = rnd.getWord(20);\r", + " event2.title = rnd.getWord(3);\r", + " pm.collectionVariables.set(\"eid\", event.id)\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: event2,\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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.have.status(200);\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}" + }, + "url": { + "raw": "{{baseUrl}}/admin/events/:eventId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "events", + ":eventId" + ], + "variable": [ + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id события" + } + ] + }, + "description": "Обратите внимание:\n - дата начала события должна быть не ранее чем за час от даты публикации.\n- событие должно быть в состоянии ожидания публикации" + }, + "response": [] + }, + { + "name": "Изменение события с description.length == 7000 && annotation.length == 2000 && title.length == 120 (admin endpoint)", + "event": [ + { + "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", + " const category = await api.addCategory(rnd.getCategory());\r", + " let event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " let event2 = rnd.getEvent(category.id)\r", + " event2.annotation = rnd.getWord(2000);\r", + " event2.description = rnd.getWord(7000);\r", + " event2.title = rnd.getWord(120);\r", + " pm.collectionVariables.set(\"eid\", event.id)\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: event2,\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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.have.status(200);\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}" + }, + "url": { + "raw": "{{baseUrl}}/admin/events/:eventId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "events", + ":eventId" + ], + "variable": [ + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id события" + } + ] + }, + "description": "Обратите внимание:\n - дата начала события должна быть не ранее чем за час от даты публикации.\n- событие должно быть в состоянии ожидания публикации" + }, + "response": [] + }, + { + "name": "Изменение заголовка события с title.length < 3 (user endpoint)", + "event": [ + { + "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", + " const category = await api.addCategory(rnd.getCategory());\r", + " const event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " pm.collectionVariables.set(\"uid\", user.id);\r", + " pm.collectionVariables.set(\"eid\", event.id);\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({\r", + " title: rnd.getWord(2)\r", + " }),\r", + " options: { raw: { language: 'json' } }\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(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/events/:eventId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events", + ":eventId" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + }, + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id отменяемого события" + } + ] + }, + "description": "Обратите внимание: Отменить можно только событие в состоянии ожидания модерации." + }, + "response": [] + }, + { + "name": "Изменение заголовка события с title.length > 120 (user endpoint)", + "event": [ + { + "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", + " const category = await api.addCategory(rnd.getCategory());\r", + " const event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " pm.collectionVariables.set(\"uid\", user.id);\r", + " pm.collectionVariables.set(\"eid\", event.id);\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({\r", + " title: rnd.getWord(121)\r", + " }),\r", + " options: { raw: { language: 'json' } }\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(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/events/:eventId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events", + ":eventId" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + }, + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id отменяемого события" + } + ] + }, + "description": "Обратите внимание: Отменить можно только событие в состоянии ожидания модерации." + }, + "response": [] + }, + { + "name": "Изменение описания события с description.length < 20 (user endpoint)", + "event": [ + { + "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", + " const category = await api.addCategory(rnd.getCategory());\r", + " const event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " pm.collectionVariables.set(\"uid\", user.id);\r", + " pm.collectionVariables.set(\"eid\", event.id);\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({\r", + " description: rnd.getWord(19)\r", + " }),\r", + " options: { raw: { language: 'json' } }\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(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/events/:eventId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events", + ":eventId" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + }, + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id отменяемого события" + } + ] + }, + "description": "Обратите внимание: Отменить можно только событие в состоянии ожидания модерации." + }, + "response": [] + }, + { + "name": "Изменение описания события с description.length > 7000 (user endpoint)", + "event": [ + { + "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", + " const category = await api.addCategory(rnd.getCategory());\r", + " const event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " pm.collectionVariables.set(\"uid\", user.id);\r", + " pm.collectionVariables.set(\"eid\", event.id);\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({\r", + " description: rnd.getWord(7001)\r", + " }),\r", + " options: { raw: { language: 'json' } }\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(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/events/:eventId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events", + ":eventId" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + }, + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id отменяемого события" + } + ] + }, + "description": "Обратите внимание: Отменить можно только событие в состоянии ожидания модерации." + }, + "response": [] + }, + { + "name": "Изменение аннотации события с annotation.length < 20 (user endpoint)", + "event": [ + { + "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", + " const category = await api.addCategory(rnd.getCategory());\r", + " const event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " pm.collectionVariables.set(\"uid\", user.id);\r", + " pm.collectionVariables.set(\"eid\", event.id);\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({\r", + " annotation: rnd.getWord(19)\r", + " }),\r", + " options: { raw: { language: 'json' } }\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(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/events/:eventId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events", + ":eventId" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + }, + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id отменяемого события" + } + ] + }, + "description": "Обратите внимание: Отменить можно только событие в состоянии ожидания модерации." + }, + "response": [] + }, + { + "name": "Изменение аннотации события с annotation.length > 2000 (user endpoint)", + "event": [ + { + "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", + " const category = await api.addCategory(rnd.getCategory());\r", + " const event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " pm.collectionVariables.set(\"uid\", user.id);\r", + " pm.collectionVariables.set(\"eid\", event.id);\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({\r", + " annotation: rnd.getWord(2001)\r", + " }),\r", + " options: { raw: { language: 'json' } }\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(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/events/:eventId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events", + ":eventId" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + }, + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id отменяемого события" + } + ] + }, + "description": "Обратите внимание: Отменить можно только событие в состоянии ожидания модерации." + }, + "response": [] + }, + { + "name": "Изменение события с description.length == 20 && annotation.length == 20 && title.length == 3 (user endpoint)", + "event": [ + { + "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", + " const category = await api.addCategory(rnd.getCategory());\r", + " const event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " pm.collectionVariables.set(\"uid\", user.id);\r", + " pm.collectionVariables.set(\"eid\", event.id);\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({\r", + " description: rnd.getWord(20),\r", + " annotation: rnd.getWord(20),\r", + " title: rnd.getWord(3)\r", + " }),\r", + " options: { raw: { language: 'json' } }\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.have.status(200);\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/events/:eventId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events", + ":eventId" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + }, + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id отменяемого события" + } + ] + }, + "description": "Обратите внимание: Отменить можно только событие в состоянии ожидания модерации." + }, + "response": [] + }, + { + "name": "Изменение события с description.length == 7000 && annotation.length == 2000 && title.length == 120 (user endpoint)", + "event": [ + { + "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", + " const category = await api.addCategory(rnd.getCategory());\r", + " const event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " pm.collectionVariables.set(\"uid\", user.id);\r", + " pm.collectionVariables.set(\"eid\", event.id);\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({\r", + " description: rnd.getWord(7000),\r", + " annotation: rnd.getWord(2000),\r", + " title: rnd.getWord(120)\r", + " }),\r", + " options: { raw: { language: 'json' } }\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.have.status(200);\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/events/:eventId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events", + ":eventId" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + }, + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id отменяемого события" + } + ] + }, + "description": "Обратите внимание: Отменить можно только событие в состоянии ожидания модерации." + }, + "response": [] + } + ] + }, + { + "name": "Default values check", + "item": [ + { + "name": "Добавление нового события без paid, participantLimit, requestModeration", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let usersArr = Array.from({length: 10}, () => rnd.getUser());\r", + " let categoriesArr = Array.from({length: 10}, () => rnd.getCategory());\r", + " let usersResponseArr = [], categoriesResponseArr = [], eventArr, eventResponseArr = [];\r", + " try {\r", + " for (const u of usersArr){\r", + " usersResponseArr.push(await api.addUser(u));\r", + " }\r", + " for (const c of categoriesArr){\r", + " categoriesResponseArr.push(await api.addCategory(c));\r", + " }\r", + " eventArr = Array.from(categoriesResponseArr, (x) => rnd.getEvent(x.id));\r", + " for (let i = 0; i < 10; i++){\r", + " delete eventArr[i].requestModeration;\r", + " delete eventArr[i].paid;\r", + " delete eventArr[i].participantLimit;\r", + " }\r", + " for (let i = 0; i < 10; i++){\r", + " eventResponseArr.push(await api.addEvent(usersResponseArr[i].id, eventArr[i]));\r", + " }\r", + " pm.collectionVariables.set('responseArr', eventResponseArr)\r", + " const user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"uid\", user.id)\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " event = rnd.getEvent(category.id);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(event),\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" + } + }, + { + "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 = pm.collectionVariables.get('responseArr');\r", + "\r", + "\r", + "pm.test(\"У каждого созданного события paid должно принять значение по умолчанию(false)\", function () {\r", + " source.forEach(function(x){pm.expect(x.paid).to.be.equal(false)});\r", + "});\r", + "\r", + "pm.test(\"У каждого созданного события participantLimit должен принять значение по умолчанию(0)\", function () {\r", + " source.forEach(function(x){pm.expect(x.participantLimit).to.be.equal(0)});\r", + "});\r", + "\r", + "pm.test(\"У каждого созданного события requestModeration должно принять значение по умолчанию(true)\", function () {\r", + " source.forEach(function(x){pm.expect(x.requestModeration).to.be.equal(true)});\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/events", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + } + ] + }, + "description": "Обратите внимание: дата и время на которые намечено событие не может быть раньше, чем через два часа от текущего момента" + }, + "response": [] + }, + { + "name": "Проверка на значения по-умолчанию from и size(event)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let user, category, eventArr;\r", + " try {\r", + " user = await api.addUser(rnd.getUser());\r", + " category = await api.addCategory(rnd.getCategory());\r", + " eventArr = Array.from({length:11}, () => rnd.getEvent(category.id));\r", + " for (let i = 0; i < 11; i++){\r", + " await api.addEvent(user.id, eventArr[i]);\r", + " }\r", + " await pm.sendRequest({\r", + " url : \"http://localhost:8080/admin/events?from=0\",\r", + " method : \"GET\",\r", + " header: { \"Content-Type\": \"application/json\" }\r", + " }, (error, response) => {pm.collectionVariables.set('source', response.json())});\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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", + "pm.test(\"Значение from по-умолчанию должно быть равным 0\", function () {\r", + " pm.expect(target[0].id).to.be.equal(source[0].id, 'Запросы с from=0 и без него должны начинаться с одного и того же события');\r", + "});\r", + "\r", + "pm.test(\"Значение size по-умолчанию должно быть равным 10\", function () {\r", + " pm.expect(target.length).to.be.equal(10);\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/admin/events", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "events" + ], + "query": [ + { + "key": "users", + "value": "0", + "description": "список id пользователей, чьи события нужно найти", + "disabled": true + }, + { + "key": "states", + "value": "PUBLISHED", + "description": "список состояний в которых находятся искомые события", + "disabled": true + }, + { + "key": "categories", + "value": "0", + "description": "список id категорий в которых будет вестись поиск", + "disabled": true + }, + { + "key": "rangeStart", + "value": "2022-01-06%2013%3A30%3A38", + "description": "дата и время не раньше которых должно произойти событие", + "disabled": true + }, + { + "key": "rangeEnd", + "value": "2097-09-06%2013%3A30%3A38", + "description": "дата и время не позже которых должно произойти событие", + "disabled": true + }, + { + "key": "from", + "value": "0", + "description": "количество событий, которые нужно пропустить для формирования текущего набора", + "disabled": true + }, + { + "key": "size", + "value": "1000", + "description": "количество событий в наборе", + "disabled": true + } + ] + }, + "description": "Эндпоинт возвращает полную информацию обо всех событиях подходящих под переданные условия" + }, + "response": [] + }, + { + "name": "Получение событий, добавленных текущим пользователем. Проверка на значения по-умолчанию size и from", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let eventArr, user, category, eventResponseArr = [];\r", + " try {\r", + " user = await api.addUser(rnd.getUser());\r", + " category = await api.addCategory(rnd.getCategory());\r", + " eventArr = Array.from({length:11}, () => rnd.getEvent(category.id));\r", + " for (let i = 0; i < 11; i++){\r", + " eventResponseArr.push(await api.addEvent(user.id, eventArr[i]));\r", + " }\r", + " pm.collectionVariables.set('responseArr', eventResponseArr)\r", + " pm.collectionVariables.set(\"uid\", user.id)\r", + " await pm.sendRequest({\r", + " url : \"http://localhost:8080/users/\" + user.id + \"/events?from=0\",\r", + " method : \"GET\",\r", + " header: { \"Content-Type\": \"application/json\" }\r", + " }, (error, response) => {pm.collectionVariables.set('source', response.json())});\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('responseArr');\r", + "newSourceArr = Array.from(source, (x) => x.id);\r", + "const responseWithFrom = pm.collectionVariables.get('source');\r", + "\r", + "pm.test(\"Значение size по-умолчанию должно быть равным 10\", function () {\r", + " pm.expect(target.length).to.equal(10);\r", + "});\r", + "\r", + "pm.test(\"Значение from по-умолчанию должно быть равным 0\", function () {\r", + " pm.expect(target[0].id).to.be.equal(responseWithFrom[0].id, 'Запросы с from=0 и без него должны начинаться с одного и того же события');\r", + "});\r", + "\r", + "pm.test(\"Все найденные события должны быть в списке добавленных\", function () {\r", + " source.forEach(function(x){pm.expect(newSourceArr).to.include(x.id)});\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/users/:userId/events", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events" + ], + "query": [ + { + "key": "from", + "value": "0", + "description": "количество элементов, которые нужно пропустить для формирования текущего набора", + "disabled": true + }, + { + "key": "size", + "value": "1000", + "description": "количество элементов в наборе", + "disabled": true + } + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + } + ] + } + }, + "response": [] + }, + { + "name": "Получение событий с возможностью фильтрации. Проверка на значение по-умолчанию size", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let user, category, eventArr, eventResponseArr = [], publishEventResponseArr = [];\r", + " try {\r", + " user = await api.addUser(rnd.getUser());\r", + " category = await api.addCategory(rnd.getCategory());\r", + " eventArr = Array.from({length:11}, () => rnd.getEvent(category.id));\r", + " for (let i = 0; i < 11; i++){\r", + " eventResponseArr.push(await api.addEvent(user.id, eventArr[i]));\r", + " }\r", + " for (let i = 0; i < 11; i++){\r", + " publishEventResponseArr.push(await api.publishEvent(eventResponseArr[i].id));\r", + " }\r", + " pm.collectionVariables.set('responseArr', eventResponseArr);\r", + " pm.collectionVariables.set('catid', category.id);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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('responseArr');\r", + "newSourceArr = Array.from(source, (x) => x.id);\r", + "\r", + "pm.test(\"Значение size по-умолчанию должно быть равным 10\", function () {\r", + " pm.expect(target.length).to.equal(10);\r", + "});\r", + "\r", + "pm.test(\"Все найденные события должны быть в списке добавленных\", function () {\r", + " source.forEach(function(x){pm.expect(newSourceArr).to.include(x.id)});\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/events?categories={{catid}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "events" + ], + "query": [ + { + "key": "text", + "value": "0", + "description": "текст для поиска в содержимом аннотации и подробном описании события", + "disabled": true + }, + { + "key": "categories", + "value": "{{catid}}", + "description": "список идентификаторов категорий в которых будет вестись поиск" + }, + { + "key": "paid", + "value": "true", + "description": "поиск только платных/бесплатных событий", + "disabled": true + }, + { + "key": "rangeStart", + "value": "2022-01-06%2013%3A30%3A38", + "description": "дата и время не раньше которых должно произойти событие", + "disabled": true + }, + { + "key": "rangeEnd", + "value": "2097-09-06%2013%3A30%3A38", + "description": "дата и время не позже которых должно произойти событие", + "disabled": true + }, + { + "key": "onlyAvailable", + "value": "false", + "description": "только события у которых не исчерпан лимит запросов на участие", + "disabled": true + }, + { + "key": "sort", + "value": "EVENT_DATE", + "description": "Вариант сортировки: по дате события или по количеству просмотров", + "disabled": true + }, + { + "key": "from", + "value": "0", + "description": "количество событий, которые нужно пропустить для формирования текущего набора", + "disabled": true + }, + { + "key": "size", + "value": "1000", + "description": "количество событий в наборе", + "disabled": true + } + ] + }, + "description": "Обратите внимание: \n- это публичный эндпоинт, соответственно в выдаче должны быть только опубликованные события\n- текстовый поиск (по аннотации и подробному описанию) должен быть без учета регистра букв\n- если в запросе не указан диапазон дат [rangeStart-rangeEnd], то нужно выгружать события, которые произойдут позже текущей даты и времени\n- информация о каждом событии должна включать в себя количество просмотров и количество уже одобренных заявок на участие\n- информацию о том, что по этому эндпоинту был осуществлен и обработан запрос, нужно сохранить в сервисе статистики" + }, + "response": [] + } + ] + } + ] + }, + { + "name": "Users", + "item": [ + { + "name": "Required query params", + "item": [] + }, + { + "name": "Unrequired query params", + "item": [ + { + "name": "Поиск пользователей без нескольких Query params", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let compilation;\r", + " try {\r", + " const user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"uid\", user.id)\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", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/admin/users?ids={{uid}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "users" + ], + "query": [ + { + "key": "ids", + "value": "{{uid}}", + "description": "id пользователей" + }, + { + "key": "from", + "value": "0", + "description": "количество элементов, которые нужно пропустить для формирования текущего набора", + "disabled": true + }, + { + "key": "size", + "value": "10", + "description": "количество элементов в наборе", + "disabled": true + } + ] + } + }, + "response": [] + }, + { + "name": "Поиск пользователей без параметра ids", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let compilation;\r", + " try {\r", + " let user1 = rnd.getUser();\r", + " user1 = await api.addUser(user1);\r", + "\r", + " let user2 = rnd.getUser()\r", + " user2 = await api.addUser(user2);\r", + " //pm.collectionVariables.set('fromId', user1.id);\r", + " pm.collectionVariables.set('source1', user1);\r", + " pm.collectionVariables.set('source2', user2);\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 source1 = pm.collectionVariables.get('source1');\r", + "const source2 = pm.collectionVariables.get('source2');\r", + "\r", + "pm.test(\"Пользователи должны содержать поля: id, name, email\", function () {\r", + " pm.expect(target[target.length-2]).to.have.property('id');\r", + " pm.expect(target[target.length-2]).to.have.property('name');\r", + " pm.expect(target[target.length-2]).to.have.property('email');\r", + " pm.expect(target[target.length-1]).to.have.property('id');\r", + " pm.expect(target[target.length-1]).to.have.property('name');\r", + " pm.expect(target[target.length-1]).to.have.property('email');\r", + "});\r", + "\r", + "pm.test(\"Данные последних двух пользователей должны совпадать с данными добавленных пользователей\", function () {\r", + " pm.expect(target[target.length-2].id).to.equal(source1.id);\r", + " pm.expect(target[target.length-2].name).to.equal(source1.name);\r", + " pm.expect(target[target.length-2].email).to.equal(source1.email);\r", + " pm.expect(target[target.length-1].id).to.equal(source2.id);\r", + " pm.expect(target[target.length-1].name).to.equal(source2.name);\r", + " pm.expect(target[target.length-1].email).to.equal(source2.email);\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/admin/users?from={{fromId}}&size=100000", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "users" + ], + "query": [ + { + "key": "ids", + "value": "{{uid}}", + "description": "id пользователей", + "disabled": true + }, + { + "key": "from", + "value": "{{fromId}}", + "description": "количество элементов, которые нужно пропустить для формирования текущего набора" + }, + { + "key": "size", + "value": "100000", + "description": "количество элементов в наборе" + } + ] + } + }, + "response": [] + }, + { + "name": "Поиск событий без нескольких Query params", + "event": [ + { + "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", + " 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.request.removeQueryParams(['users', 'categories']);\r", + " pm.request.addQueryParams([`users=` + user.id, 'categories=' + category.id]);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/admin/events", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "events" + ], + "query": [ + { + "key": "users", + "value": "0", + "description": "список id пользователей, чьи события нужно найти", + "disabled": true + }, + { + "key": "states", + "value": "PUBLISHED", + "description": "список состояний в которых находятся искомые события", + "disabled": true + }, + { + "key": "categories", + "value": "0", + "description": "список id категорий в которых будет вестись поиск", + "disabled": true + }, + { + "key": "rangeStart", + "value": "2022-01-06%2013%3A30%3A38", + "description": "дата и время не раньше которых должно произойти событие", + "disabled": true + }, + { + "key": "rangeEnd", + "value": "2097-09-06%2013%3A30%3A38", + "description": "дата и время не позже которых должно произойти событие", + "disabled": true + }, + { + "key": "from", + "value": "0", + "description": "количество событий, которые нужно пропустить для формирования текущего набора", + "disabled": true + }, + { + "key": "size", + "value": "1000", + "description": "количество событий в наборе", + "disabled": true + } + ] + }, + "description": "Эндпоинт возвращает полную информацию обо всех событиях подходящих под переданные условия" + }, + "response": [] + } + ] + }, + { + "name": "Required params in body", + "item": [ + { + "name": "Добавление пользователя с электронной почтой, состоящей только из пробелов", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let user;\r", + " try {\r", + " user = rnd.getUser();\r", + " user.email = \" \";\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(user),\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" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400\", function () {\r", + " pm.response.to.have.status(400);\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/users", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "users" + ] + } + }, + "response": [] + }, + { + "name": "Добавление пользователя с пустой электронной почтой", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let user;\r", + " try {\r", + " user = rnd.getUser();\r", + " user.email = \"\";\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(user),\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" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400\", function () {\r", + " pm.response.to.have.status(400);\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/users", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "users" + ] + } + }, + "response": [] + }, + { + "name": "Добавление пользователя без поля email", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let user;\r", + " try {\r", + " user = rnd.getUser();\r", + " delete user.email;\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(user),\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" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400\", function () {\r", + " pm.response.to.have.status(400);\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/users", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "users" + ] + } + }, + "response": [] + }, + { + "name": "Добавление пользователя с пустым именем", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let user;\r", + " try {\r", + " user = rnd.getUser();\r", + " user.name = \"\";\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(user),\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" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400\", function () {\r", + " pm.response.to.have.status(400);\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/users", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "users" + ] + } + }, + "response": [] + }, + { + "name": "Добавление пользователя с именем, состоящим только из пробелов", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let user;\r", + " try {\r", + " user = rnd.getUser();\r", + " user.name = \" \";\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(user),\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" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400\", function () {\r", + " pm.response.to.have.status(400);\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/users", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "users" + ] + } + }, + "response": [] + }, + { + "name": "Добавление пользователя без поля name", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let user;\r", + " try {\r", + " user = rnd.getUser();\r", + " delete user.name;\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(user),\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" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400\", function () {\r", + " pm.response.to.have.status(400);\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/users", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "users" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Misc tests", + "item": [] + }, + { + "name": "String length restrictions", + "item": [ + { + "name": "Добавление пользователя с name.length < 2", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let user;\r", + " try {\r", + " user = rnd.getUser();\r", + " user.name = rnd.getWord(1);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(user),\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" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/users", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "users" + ] + } + }, + "response": [] + }, + { + "name": "Добавление пользователя с name.length == 2", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let user;\r", + " try {\r", + " user = rnd.getUser();\r", + " user.name = rnd.getWord(2);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(user),\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" + } + }, + { + "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", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/users", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "users" + ] + } + }, + "response": [] + }, + { + "name": "Добавление пользователя с name.length > 250", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let user;\r", + " try {\r", + " user = rnd.getUser();\r", + " user.name = rnd.getWord(251);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(user),\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" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/users", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "users" + ] + } + }, + "response": [] + }, + { + "name": "Добавление пользователя с name.length == 250", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let user;\r", + " try {\r", + " user = rnd.getUser();\r", + " user.name = rnd.getWord(250);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(user),\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" + } + }, + { + "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", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/users", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "users" + ] + } + }, + "response": [] + }, + { + "name": "Добавление пользователя с email.length < 6", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let user;\r", + " try {\r", + " user = rnd.getUser();\r", + " user.email = rnd.getWord(1) + '@a.r';\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(user),\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" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/users", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "users" + ] + } + }, + "response": [] + }, + { + "name": "Добавление пользователя с email.length == 6", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let user;\r", + " try {\r", + " user = rnd.getUser();\r", + " user.email = rnd.getWord(1) + '@a.ru';\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(user),\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" + } + }, + { + "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", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/users", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "users" + ] + } + }, + "response": [] + }, + { + "name": "Добавление пользователя с email.localpart.length > 64", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let user;\r", + " try {\r", + " user = rnd.getUser();\r", + " user.email = rnd.getWord(65) + '@a.ru';\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(user),\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" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/users", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "users" + ] + } + }, + "response": [] + }, + { + "name": "Добавление пользователя с email.localpart.length == 64", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let user;\r", + " try {\r", + " user = rnd.getUser();\r", + " user.email = rnd.getWord(59) + '@a.ru';\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(user),\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" + } + }, + { + "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", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/users", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "users" + ] + } + }, + "response": [] + }, + { + "name": "Добавление пользователя с domain.part.length > 63", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let user;\r", + " try {\r", + " user = rnd.getUser();\r", + " user.email = rnd.getWord(1) + '@' + rnd.getWord(64) + '.ru';\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(user),\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" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/users", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "users" + ] + } + }, + "response": [] + }, + { + "name": "Добавление пользователя с domain.part.length == 63", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let user;\r", + " try {\r", + " user = rnd.getUser();\r", + " user.email = rnd.getWord(1) + '@' + rnd.getWord(60) + '.ru';\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(user),\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" + } + }, + { + "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", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/users", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "users" + ] + } + }, + "response": [] + }, + { + "name": "Добавление пользователя с email.length > 254", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let user;\r", + " try {\r", + " user = rnd.getUser();\r", + " user.email = rnd.getWord(1) + '@' + rnd.getWord(63) + '.' + rnd.getWord(63) + '.' + rnd.getWord(63) + '.' + rnd.getWord(61);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(user),\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" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/users", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "users" + ] + } + }, + "response": [] + }, + { + "name": "Добавление пользователя с email.length == 254", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let user;\r", + " try {\r", + " user = rnd.getUser();\r", + " user.email = rnd.getWord(1) + '@' + rnd.getWord(63) + '.' + rnd.getWord(63) + '.' + rnd.getWord(63) + '.' + rnd.getWord(60);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(user),\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" + } + }, + { + "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", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/users", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "users" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Default values check", + "item": [ + { + "name": "Проверка на значения по-умолчанию from и size(user)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " for (let i = 0; i < 11; i++){\r", + " await api.addUser(rnd.getUser());\r", + " }\r", + " await pm.sendRequest({\r", + " url : \"http://localhost:8080/admin/users?from=0\",\r", + " method : \"GET\",\r", + " header: { \"Content-Type\": \"application/json\" }\r", + " }, (error, response) => {pm.collectionVariables.set('source', response.json())});\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", + "pm.test(\"Значение from по-умолчанию должно быть равным 0\", function () {\r", + " pm.expect(target[0].id).to.be.equal(source[0].id, 'Запросы с from=0 и без него должны начинаться с одного и того же события');\r", + "});\r", + "\r", + "pm.test(\"Значение size по-умолчанию должно быть равным 10\", function () {\r", + " pm.expect(target.length).to.be.equal(10);\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/admin/users", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "users" + ], + "query": [ + { + "key": "ids", + "value": "{{uid}}", + "description": "id пользователей", + "disabled": true + }, + { + "key": "ids", + "value": "-10833646", + "description": "id пользователей", + "disabled": true + }, + { + "key": "from", + "value": "0", + "description": "количество элементов, которые нужно пропустить для формирования текущего набора", + "disabled": true + }, + { + "key": "size", + "value": "10", + "description": "количество элементов в наборе", + "disabled": true + } + ] + } + }, + "response": [] + } + ] + } + ] + }, + { + "name": "Category", + "item": [ + { + "name": "Required query params", + "item": [] + }, + { + "name": "Unrequired params in body", + "item": [ + { + "name": "Получение категорий без нескольких Query params", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "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", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/categories", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "categories" + ], + "query": [ + { + "key": "from", + "value": "0", + "description": "количество категорий, которые нужно пропустить для формирования текущего набора", + "disabled": true + }, + { + "key": "size", + "value": "10000", + "description": "количество категорий в наборе", + "disabled": true + } + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Required params in body", + "item": [ + { + "name": "Добавление категории с именем, состоящим из пробелов", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let category;\r", + " try {\r", + " category = {name: ' '};\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(category),\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" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{baseUrl}}/admin/categories", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "categories" + ] + } + }, + "response": [] + }, + { + "name": "Добавление категории с пустым полем name", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let category;\r", + " try {\r", + " category = {name: ''};\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(category),\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" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{baseUrl}}/admin/categories", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "categories" + ] + } + }, + "response": [] + }, + { + "name": "Добавление категории без поля name", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let category;\r", + " try {\r", + " category = {};\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(category),\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" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{baseUrl}}/admin/categories", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "categories" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Misc tests", + "item": [ + { + "name": "Изменение категории с неизменными данными", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + " let category, categoryObj\r", + " try {\r", + " category = rnd.getCategory();\r", + " categoryObj = await api.addCategory(category);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + " pm.collectionVariables.set(\"catid\", Number(categoryObj.id))\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(category),\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" + } + }, + { + "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 = JSON.parse(pm.request.body.raw);\r", + "const target = pm.response.json();\r", + "\r", + "pm.test(\"Категория должна содержать поля: id, name\", function () {\r", + "pm.expect(target).to.have.property('id');\r", + "pm.expect(target).to.have.property('name');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(target.id).to.not.be.null;\r", + " pm.expect(source.name).equal(target.name, 'Название категории должно совпадать с отправленным');\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [], + "body": { + "mode": "raw", + "raw": "{{request_body}}" + }, + "url": { + "raw": "{{baseUrl}}/admin/categories/:catId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "categories", + ":catId" + ], + "variable": [ + { + "key": "catId", + "value": "{{catid}}" + } + ] + } + }, + "response": [] + } + ] + }, + { + "name": "String length restrictions", + "item": [ + { + "name": "Добавление новой категории с name.length > 50", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let category;\r", + " try {\r", + " category = {'name': rnd.getWord(51)};\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(category),\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" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/categories", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "categories" + ] + }, + "description": "Обратите внимание: имя категории должно быть уникальным" + }, + "response": [] + }, + { + "name": "Добавление новой категории с name.length == 50", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let category;\r", + " try {\r", + " category = {'name': rnd.getWord(50)};\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(category),\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" + } + }, + { + "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", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/categories", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "categories" + ] + }, + "description": "Обратите внимание: имя категории должно быть уникальным" + }, + "response": [] + }, + { + "name": "Изменение имени категории с name.length > 50", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + " let category\r", + " try {\r", + " category = await api.addCategory(rnd.getCategory());\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + " pm.collectionVariables.set(\"catid\", Number(category.id))\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({\r", + " name : rnd.getWord(51)\r", + " }),\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" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [], + "body": { + "mode": "raw", + "raw": "{{request_body}}" + }, + "url": { + "raw": "{{baseUrl}}/admin/categories/:catId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "categories", + ":catId" + ], + "variable": [ + { + "key": "catId", + "value": "{{catid}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Изменение имени категории с name.length == 50", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + " let category\r", + " try {\r", + " category = await api.addCategory(rnd.getCategory());\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + " pm.collectionVariables.set(\"catid\", Number(category.id))\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({\r", + " name : rnd.getWord(50)\r", + " }),\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" + } + }, + { + "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", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [], + "body": { + "mode": "raw", + "raw": "{{request_body}}" + }, + "url": { + "raw": "{{baseUrl}}/admin/categories/:catId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "categories", + ":catId" + ], + "variable": [ + { + "key": "catId", + "value": "{{catid}}" + } + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Default values check", + "item": [ + { + "name": "Проверка на значения по-умолчанию from и size(category)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " for (let i = 0; i < 11; i++){\r", + " await api.addCategory(rnd.getCategory());\r", + " }\r", + " await pm.sendRequest({\r", + " url : \"http://localhost:8080/categories?from=0\",\r", + " method : \"GET\",\r", + " header: { \"Content-Type\": \"application/json\" }\r", + " }, (error, response) => {pm.collectionVariables.set('source', response.json())});\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", + "pm.test(\"Значение from по-умолчанию должно быть равным 0\", function () {\r", + " pm.expect(target[0].id).to.be.equal(source[0].id, 'Запросы с from=0 и без него должны начинаться с одного и того же события');\r", + "});\r", + "\r", + "pm.test(\"Значение size по-умолчанию должно быть равным 10\", function () {\r", + " pm.expect(target.length).to.be.equal(10);\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/categories", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "categories" + ], + "query": [ + { + "key": "from", + "value": "0", + "description": "количество категорий, которые нужно пропустить для формирования текущего набора", + "disabled": true + }, + { + "key": "size", + "value": "1000", + "description": "количество категорий в наборе", + "disabled": true + } + ] + } + }, + "response": [] + } + ] + } + ] + }, + { + "name": "Compilation", + "item": [ + { + "name": "Required query params", + "item": [] + }, + { + "name": "Unreqired params in body", + "item": [ + { + "name": "Получение подборок событий без нескольких Query params", + "event": [ + { + "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", + " const category = await api.addCategory(rnd.getCategory());\r", + " const compilation = await api.addCompilation(rnd.getCompilation());\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/compilations", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "compilations" + ], + "query": [ + { + "key": "pinned", + "value": "true", + "description": "искать только закрепленные/не закрепленные подборки", + "disabled": true + }, + { + "key": "from", + "value": "0", + "description": "количество элементов, которые нужно пропустить для формирования текущего набора", + "disabled": true + }, + { + "key": "size", + "value": "1000", + "description": "количество элементов в наборе", + "disabled": true + } + ] + } + }, + "response": [] + }, + { + "name": "Добавление новой подборки без параметра pinned", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let compilation;\r", + " try {\r", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " const event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " compilation = rnd.getCompilation(event.id);\r", + " delete compilation['pinned'];\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(compilation),\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" + } + }, + { + "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, title, pinned, events\", function () {\r", + "pm.expect(target).to.have.property('id');\r", + "pm.expect(target).to.have.property('title');\r", + "pm.expect(target).to.have.property('pinned');\r", + "pm.expect(target).to.have.property('events');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(target.id).to.not.be.null;\r", + " pm.expect(target.title).to.be.a(\"string\");\r", + " pm.expect(target.events).to.be.an(\"array\");\r", + " pm.expect(target.pinned).equal(false);\r", + "\r", + " pm.expect(source.events[0]).equal(target.events[0].id, 'Идентификаторы событий в подборке должен быть идентичен идентификаторам, указанным при создании подборки ');\r", + " pm.expect(source.title).equal(target.title, 'Название подборки должно соответствовать указанному при создании');\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/compilations", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "compilations" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Required params in body", + "item": [ + { + "name": "Добавление подборки без поля title", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let compilation;\r", + " try {\r", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " compilation = {\r", + " \"pinned\":\"true\"};\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(compilation),\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" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/compilations", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "compilations" + ] + } + }, + "response": [] + }, + { + "name": "Добавление подборки с пустым полем title", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let compilation;\r", + " try {\r", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " compilation = {\r", + " \"pinned\":\"true\",\r", + " \"title\": \"\"};\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(compilation),\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" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/compilations", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "compilations" + ] + } + }, + "response": [] + }, + { + "name": "Добавление подборки с пустой строкой в качестве названия", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let compilation;\r", + " try {\r", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " compilation = {\r", + " \"pinned\":\"true\",\r", + " \"title\": \" \"};\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(compilation),\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" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/compilations", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "compilations" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Misc tests", + "item": [ + { + "name": "Добавление подборки с проверкой связей многие-ко-многим", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let compilation;\r", + " let compilation2;\r", + " try {\r", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " const event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " const event2 = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " compilation = rnd.getCompilation(event.id, event2.id);\r", + " compilation2 = rnd.getCompilation(event.id, event2.id);\r", + " await pm.sendRequest({\r", + " url : \"http://localhost:8080/admin/compilations/\",\r", + " method : \"POST\",\r", + " header: { \"Content-Type\": \"application/json\" },\r", + " body: JSON.stringify({\r", + " events: compilation2.events,\r", + " title: compilation2.title\r", + " })\r", + " }, (error, response) => {\r", + "\r", + " });\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(compilation),\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" + } + }, + { + "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, title, pinned, events\", function () {\r", + "pm.expect(target).to.have.property('id');\r", + "pm.expect(target).to.have.property('title');\r", + "pm.expect(target).to.have.property('pinned');\r", + "pm.expect(target).to.have.property('events');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(target.id).to.not.be.null;\r", + " pm.expect(target.title).to.be.a(\"string\");\r", + " pm.expect(target.events).to.be.an(\"array\");\r", + " if (target.events[0].id < target.events[1].id){\r", + " pm.expect(source.events[0]).equal(target.events[0].id, 'Идентификаторы событий в подборке должен быть идентичен идентификаторам, указанным при создании подборки ');\r", + " } else {\r", + " pm.expect(source.events[0]).equal(target.events[1].id, 'Идентификаторы событий в подборке должен быть идентичен идентификаторам, указанным при создании подборки ');\r", + " }\r", + " pm.expect(source.title).equal(target.title, 'Название подборки должно соответствовать указанному при создании');\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/compilations", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "compilations" + ] + } + }, + "response": [] + }, + { + "name": "Добавление подборки без событий", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let compilation;\r", + " try {\r", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " compilation = rnd.getCompilation();\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(compilation),\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" + } + }, + { + "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, title, pinned, events\", function () {\r", + " pm.expect(target).to.have.property('id');\r", + " pm.expect(target).to.have.property('title');\r", + " pm.expect(target).to.have.property('pinned');\r", + " pm.expect(target).to.have.property('events');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(target.id).to.not.be.null;\r", + " pm.expect(target.title).to.be.a(\"string\");\r", + " pm.expect(target.events).to.be.an(\"array\");\r", + "\r", + " pm.expect(source.title).equal(target.title, 'Название подборки должно соответствовать указанному при создании');\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/compilations", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "compilations" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "String length restrictions", + "item": [ + { + "name": "Добавление подборки с title.length > 50", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let compilation;\r", + " try {\r", + "\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({title: rnd.getWord(51)}),\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" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/compilations", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "compilations" + ] + } + }, + "response": [] + }, + { + "name": "Добавление подборки с title.length == 50", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let compilation;\r", + " try {\r", + "\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({title: rnd.getWord(50)}),\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" + } + }, + { + "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", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/compilations", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "compilations" + ] + } + }, + "response": [] + }, + { + "name": "Обновить названия подборки с title.length > 50", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " const compilation = await api.addCompilation(rnd.getCompilation());\r", + " pm.collectionVariables.set('compid', compilation.id);\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({\r", + " title: rnd.getWord(51)\r", + " }),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [], + "body": { + "mode": "raw", + "raw": "{{request_body}}" + }, + "url": { + "raw": "{{baseUrl}}/admin/compilations/:compId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "compilations", + ":compId" + ], + "variable": [ + { + "key": "compId", + "value": "{{compid}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Обновить названия подборки с title.length == 50", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " const compilation = await api.addCompilation(rnd.getCompilation());\r", + " pm.collectionVariables.set('compid', compilation.id);\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({\r", + " title: rnd.getWord(50)\r", + " }),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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.have.status(200);\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [], + "body": { + "mode": "raw", + "raw": "{{request_body}}" + }, + "url": { + "raw": "{{baseUrl}}/admin/compilations/:compId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "compilations", + ":compId" + ], + "variable": [ + { + "key": "compId", + "value": "{{compid}}" + } + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Default values check", + "item": [ + { + "name": "Добавление подборки без pinned", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let cArr = Array.from({length: 10}, () => rnd.getCompilation());\r", + " let responseArr = [];\r", + " try {\r", + " cArr.forEach(function(x){ delete x.pinned });\r", + " for (const c of cArr){\r", + " responseArr.push(await api.addCompilation(c));\r", + " }\r", + " pm.collectionVariables.set('responseArr', responseArr);\r", + " compilation = rnd.getCompilation();\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(compilation),\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" + } + }, + { + "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 = pm.collectionVariables.get('responseArr');\r", + "\r", + "\r", + "pm.test(\"У каждой из созданных подборок pinned должно принять значение по умолчанию(false)\", function () {\r", + " source.forEach(function(x){pm.expect(x.pinned).to.be.equal(false)});\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/compilations", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "compilations" + ] + } + }, + "response": [] + }, + { + "name": "Проверка на значения по-умолчанию from и size(compilation)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " for (let i = 0; i < 11; i++){\r", + " await api.addCompilation(rnd.getCompilation());\r", + " }\r", + " await pm.sendRequest({\r", + " url : \"http://localhost:8080/compilations?from=0\",\r", + " method : \"GET\",\r", + " header: { \"Content-Type\": \"application/json\" }\r", + " }, (error, response) => {pm.collectionVariables.set('source', response.json())});\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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", + "pm.test(\"Значение from по-умолчанию должно быть равным 0\", function () {\r", + " pm.expect(target[0].id).to.be.equal(source[0].id, 'Запросы с from=0 и без него должны начинаться с одного и того же события');\r", + "});\r", + "\r", + "pm.test(\"Значение size по-умолчанию должно быть равным 10\", function () {\r", + " pm.expect(target.length).to.be.equal(10);\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/compilations", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "compilations" + ], + "query": [ + { + "key": "pinned", + "value": "true", + "description": "искать только закрепленные/не закрепленные подборки", + "disabled": true + }, + { + "key": "from", + "value": "0", + "description": "количество элементов, которые нужно пропустить для формирования текущего набора", + "disabled": true + }, + { + "key": "size", + "value": "1000", + "description": "количество элементов в наборе", + "disabled": true + } + ] + } + }, + "response": [] + } + ] + } + ] + } + ] + }, + { + "name": "409 Conflict", + "item": [ + { + "name": "Попытка изменения имени категории на уже существующее", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + " let category1, category2\r", + " try {\r", + " category1 = await api.addCategory(rnd.getCategory());\r", + " category2 = await api.addCategory(rnd.getCategory());\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + " pm.collectionVariables.set(\"catid\", category2.id)\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({\r", + " name : category1.name\r", + " }),\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" + } + }, + { + "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" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/categories/:catId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "categories", + ":catId" + ], + "variable": [ + { + "key": "catId", + "value": "{{catid}}" + } + ] + }, + "description": "Обратите внимание: имя категории должно быть уникальным" + }, + "response": [] + }, + { + "name": "Добавление новой категории с занятым именем", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let category;\r", + " try {\r", + " category = rnd.getCategory();\r", + " await api.addCategory(category);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(category),\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" + } + }, + { + "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" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/categories", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "categories" + ] + }, + "description": "Обратите внимание: имя категории должно быть уникальным" + }, + "response": [] + }, + { + "name": "Добавление пользователя с занятым именем почты", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let user;\r", + " try {\r", + " user = rnd.getUser();\r", + " user.name = rnd.getWord(10);\r", + " await api.addUser(user);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(user),\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" + } + }, + { + "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" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/users", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "users" + ] + } + }, + "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", + " const category = await api.addCategory(rnd.getCategory());\r", + " const user = await api.addUser(rnd.getUser());\r", + " const event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " pm.collectionVariables.set('catid', category.id);\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(\"Ответ должен содержать код статуса 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" + } + } + ], + "request": { + "method": "DELETE", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/admin/categories/:catId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "categories", + ":catId" + ], + "variable": [ + { + "key": "catId", + "value": "{{catid}}" + } + ] + }, + "description": "Обратите внимание: с категорий не должно быть связано ни одного события." + }, + "response": [] + }, + { + "name": "Изменение имени категории на уже занятое", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + " let category1, category2\r", + " try {\r", + " category1 = await api.addCategory(rnd.getCategory());\r", + " category2 = await api.addCategory(rnd.getCategory());\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + " pm.collectionVariables.set(\"catid\", Number(category1.id))\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({\r", + " name : category2.name\r", + " }),\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" + } + }, + { + "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" + } + } + ], + "request": { + "method": "PATCH", + "header": [], + "body": { + "mode": "raw", + "raw": "{{request_body}}" + }, + "url": { + "raw": "{{baseUrl}}/admin/categories/:catId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "categories", + ":catId" + ], + "variable": [ + { + "key": "catId", + "value": "{{catid}}" + } + ] + } + }, + "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", + " const user = await api.addUser(rnd.getUser());\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", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({\r", + " stateAction : \"PUBLISH_EVENT\"\r", + " }),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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(\"Ответ должен содержать код статуса 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" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}" + }, + "url": { + "raw": "{{baseUrl}}/admin/events/:eventId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "events", + ":eventId" + ], + "variable": [ + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id события" + } + ] + }, + "description": "Обратите внимание:\n - дата начала события должна быть не ранее чем за час от даты публикации.\n- событие должно быть в состоянии ожидания публикации" + }, + "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", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " let event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " event = await api.rejectEvent(event.id);\r", + " pm.collectionVariables.set(\"eid\", event.id)\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({\r", + " stateAction : \"PUBLISH_EVENT\"\r", + " }),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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(\"Ответ должен содержать код статуса 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" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}" + }, + "url": { + "raw": "{{baseUrl}}/admin/events/:eventId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "events", + ":eventId" + ], + "variable": [ + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id события" + } + ] + }, + "description": "Обратите внимание:\n - дата начала события должна быть не ранее чем за час от даты публикации.\n- событие должно быть в состоянии ожидания публикации" + }, + "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", + " const user = await api.addUser(rnd.getUser());\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", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({\r", + " stateAction : \"REJECT_EVENT\"\r", + " }),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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(\"Ответ должен содержать код статуса 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" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}" + }, + "url": { + "raw": "{{baseUrl}}/admin/events/:eventId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "events", + ":eventId" + ], + "variable": [ + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id события" + } + ] + }, + "description": "Обратите внимание:\n - дата начала события должна быть не ранее чем за час от даты публикации.\n- событие должно быть в состоянии ожидания публикации" + }, + "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", + " const user = await api.addUser(rnd.getUser());\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", + " const submittedUser = await api.addUser(rnd.getUser());\r", + " await api.publishParticipationRequest(event.id, submittedUser.id);\r", + " pm.request.removeQueryParams(['eventId']);\r", + " pm.request.addQueryParams([`eventId=` + event.id]);\r", + " pm.collectionVariables.set('uid', submittedUser.id);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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(\"Ответ должен содержать код статуса 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" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/users/:userId/requests?eventId=0", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "requests" + ], + "query": [ + { + "key": "eventId", + "value": "0", + "description": "(Required) id события" + } + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + } + ] + }, + "description": "Обратите внимание:\n- нельзя добавить повторный запрос\n- инициатор события не может добавить запрос на участие в своём событии\n- нельзя участвовать в неопубликованном событии\n- если у события достигнут лимит запросов на участие - необходимо вернуть ошибку\n- если для события отключена пре-модерация запросов на участие, то запрос должен автоматически перейти в состояние подтвержденного" + }, + "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", + " const user = await api.addUser(rnd.getUser());\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.request.removeQueryParams(['eventId']);\r", + " pm.request.addQueryParams([`eventId=` + event.id]);\r", + " pm.collectionVariables.set('uid', user.id);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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(\"Ответ должен содержать код статуса 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" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/users/:userId/requests?eventId=0", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "requests" + ], + "query": [ + { + "key": "eventId", + "value": "0", + "description": "(Required) id события" + } + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + } + ] + }, + "description": "Обратите внимание:\n- нельзя добавить повторный запрос\n- инициатор события не может добавить запрос на участие в своём событии\n- нельзя участвовать в неопубликованном событии\n- если у события достигнут лимит запросов на участие - необходимо вернуть ошибку\n- если для события отключена пре-модерация запросов на участие, то запрос должен автоматически перейти в состояние подтвержденного" + }, + "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", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " const event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " const submittedUser = await api.addUser(rnd.getUser());\r", + " pm.request.removeQueryParams(['eventId']);\r", + " pm.request.addQueryParams([`eventId=` + event.id]);\r", + " pm.collectionVariables.set('uid', submittedUser.id);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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(\"Ответ должен содержать код статуса 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" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/users/:userId/requests?eventId=0", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "requests" + ], + "query": [ + { + "key": "eventId", + "value": "0", + "description": "(Required) id события" + } + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + } + ] + }, + "description": "Обратите внимание:\n- нельзя добавить повторный запрос\n- инициатор события не может добавить запрос на участие в своём событии\n- нельзя участвовать в неопубликованном событии\n- если у события достигнут лимит запросов на участие - необходимо вернуть ошибку\n- если для события отключена пре-модерация запросов на участие, то запрос должен автоматически перейти в состояние подтвержденного" + }, + "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", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " let eventBody = rnd.getEvent(category.id);\r", + " eventBody.participantLimit = 1;\r", + " eventBody.requestModeration = false;\r", + " let event = await api.addEvent(user.id, eventBody);\r", + " event = await api.publishEvent(event.id);\r", + " const submittedUser1 = await api.addUser(rnd.getUser());\r", + " const submittedUser2 = await api.addUser(rnd.getUser());\r", + " await api.publishParticipationRequest(event.id, submittedUser1.id);\r", + " pm.request.removeQueryParams(['eventId']);\r", + " pm.request.addQueryParams([`eventId=` + event.id]);\r", + " pm.collectionVariables.set('uid', submittedUser2.id);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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(\"Ответ должен содержать код статуса 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" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/users/:userId/requests?eventId=0", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "requests" + ], + "query": [ + { + "key": "eventId", + "value": "0", + "description": "(Required) id события" + } + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + } + ] + }, + "description": "Обратите внимание:\n- нельзя добавить повторный запрос\n- инициатор события не может добавить запрос на участие в своём событии\n- нельзя участвовать в неопубликованном событии\n- если у события достигнут лимит запросов на участие - необходимо вернуть ошибку\n- если для события отключена пре-модерация запросов на участие, то запрос должен автоматически перейти в состояние подтвержденного" + }, + "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", + " const user = await api.addUser(rnd.getUser());\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(\"uid\", user.id);\r", + " pm.collectionVariables.set(\"eid\", event.id);\r", + " pm.collectionVariables.set(\"response\", event);\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({\r", + " eventDate : \"2124-10-11 23:10:05\"\r", + " }),\r", + " options: { raw: { language: 'json' } }\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(\"Ответ должен содержать код статуса 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" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/events/:eventId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events", + ":eventId" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + }, + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id отменяемого события" + } + ] + }, + "description": "Обратите внимание: Отменить можно только событие в состоянии ожидания модерации." + }, + "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", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " let eventBody = rnd.getEvent(category.id);\r", + " eventBody['requestModeration'] = true;\r", + " eventBody['participantLimit'] = 1;\r", + " let event = await api.addEvent(user.id, eventBody);\r", + " event = await api.publishEvent(event.id);\r", + " const submittedUser1 = await api.addUser(rnd.getUser());\r", + " const submittedUser2 = await api.addUser(rnd.getUser());\r", + " const requestToParticipate = await api.publishParticipationRequest(event.id, submittedUser1.id);\r", + " const requestToParticipate2 = await api.publishParticipationRequest(event.id, submittedUser2.id);\r", + " await api.acceptParticipationRequest(event.id, user.id, requestToParticipate.id);\r", + " pm.collectionVariables.set('uid', user.id);\r", + " pm.collectionVariables.set('eid', event.id);\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({requestIds: [requestToParticipate2.id],\r", + " status:\"CONFIRMED\"}),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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(\"Ответ должен содержать код статуса 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" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/events/:eventId/requests", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events", + ":eventId", + "requests" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + }, + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id события текущего пользователя" + } + ] + }, + "description": "Обратите внимание:\n- если для события лимит заявок равен 0 или отключена пре-модерация заявок, то подтверждение заявок не требуется\n- нельзя подтвердить заявку, если уже достигнут лимит по заявкам на данное событие\n- статус можно изменить только у заявок, находящихся в состоянии ожидания\n- если при подтверждении данной заявки, лимит заявок для события исчерпан, то все неподтверждённые заявки необходимо отклонить" + }, + "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", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " let eventBody = rnd.getEvent(category.id);\r", + " eventBody['requestModeration'] = true;\r", + " eventBody['participantLimit'] = 1;\r", + " let event = await api.addEvent(user.id, eventBody);\r", + " event = await api.publishEvent(event.id);\r", + " const submittedUser = await api.addUser(rnd.getUser());\r", + " const requestToParticipate = await api.publishParticipationRequest(event.id, submittedUser.id);\r", + " await api.acceptParticipationRequest(event.id, user.id, requestToParticipate.id);\r", + " pm.collectionVariables.set('uid', user.id);\r", + " pm.collectionVariables.set('eid', event.id);\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({requestIds: [requestToParticipate.id],\r", + " status:\"REJECTED\"}),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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(\"Ответ должен содержать код статуса 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" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/events/:eventId/requests", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events", + ":eventId", + "requests" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + }, + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id события текущего пользователя" + } + ] + }, + "description": "Обратите внимание:\n- если для события лимит заявок равен 0 или отключена пре-модерация заявок, то подтверждение заявок не требуется\n- нельзя подтвердить заявку, если уже достигнут лимит по заявкам на данное событие\n- статус можно изменить только у заявок, находящихся в состоянии ожидания\n- если при подтверждении данной заявки, лимит заявок для события исчерпан, то все неподтверждённые заявки необходимо отклонить" + }, + "response": [] + } + ] + }, + { + "name": "Category", + "item": [ + { + "name": "Добавление новой категории", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let category;\r", + " try {\r", + " category = rnd.getCategory();\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(category),\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" + } + }, + { + "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, name\", function () {\r", + "pm.expect(target).to.have.property('id');\r", + "pm.expect(target).to.have.property('name');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(target.id).to.not.be.null;\r", + " pm.expect(source.name).equal(target.name, 'Название категории должно совпадать с отправленным');\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/categories", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "categories" + ] + }, + "description": "Обратите внимание: имя категории должно быть уникальным" + }, + "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", + " const category = await api.addCategory(rnd.getCategory());\r", + " pm.collectionVariables.set(\"response\", category)\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 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, name\", function () {\r", + "pm.expect(target[0]).to.have.property('id');\r", + "pm.expect(target[0]).to.have.property('name');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(source.id).equal(founded.id, 'Идентификатор категории должен соответствовать идентификатору категории добавленной ранее');\r", + " pm.expect(source.name).equal(founded.name, 'Название категории должно соответствовать названию категории добавленной ранее');\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/categories?from=0&size=1000", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "categories" + ], + "query": [ + { + "key": "from", + "value": "0", + "description": "количество категорий, которые нужно пропустить для формирования текущего набора" + }, + { + "key": "size", + "value": "1000", + "description": "количество категорий в наборе" + } + ] + } + }, + "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", + " const category = await api.addCategory(rnd.getCategory());\r", + " pm.collectionVariables.set(\"response\", category)\r", + " pm.collectionVariables.set(\"catid\", category.id)\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 source = pm.collectionVariables.get('response');\r", + "const target = pm.response.json();\r", + "\r", + "pm.test(\"Категория должна содержать поля: id, name\", function () {\r", + "pm.expect(target).to.have.property('id');\r", + "pm.expect(target).to.have.property('name');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(source.id).equal(target.id, 'Идентификатор категории должен соответствовать идентификатору в запросе');\r", + " pm.expect(source.name).equal(target.name, 'Название категории должно соответствовать названию категории с указанным идентификатором');\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/categories/:catId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "categories", + ":catId" + ], + "variable": [ + { + "key": "catId", + "value": "{{catid}}", + "description": "(Required) id категории" + } + ] + } + }, + "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", + " const category = await api.addCategory(rnd.getCategory());\r", + " const findedCategory = await api.findCategory(category.id);\r", + " pm.collectionVariables.set(\"catid\", category.id)\r", + " pm.collectionVariables.set(\"response\", findedCategory)\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(\"Ответ должен содержать код статуса 204\", function () {\r", + " pm.response.to.have.status(204);\r", + "});\r", + "\r", + "source = pm.collectionVariables.get('response');\r", + "catId = pm.collectionVariables.get('catid');\r", + "\r", + "pm.test(\"Категория должна быть найдена до удаления\", function () {\r", + " pm.expect(source.id).equal(catId, 'Идентификтор категории должен совпадать с удаляемым');\r", + "});\r", + "\r", + "pm.sendRequest({\r", + " url: pm.collectionVariables.get(\"baseUrl\") + \"/categories/\" + catId,\r", + " method: 'GET',\r", + " }, (error, response) => {\r", + " pm.test(\"Категория не должна быть найдена после удаления\", function () {\r", + " pm.expect(response.code).to.eql(404);\r", + " });\r", + " });" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "DELETE", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/admin/categories/:catId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "categories", + ":catId" + ], + "variable": [ + { + "key": "catId", + "value": "{{catid}}" + } + ] + }, + "description": "Обратите внимание: с категорий не должно быть связано ни одного события." + }, + "response": [] + }, + { + "name": "Изменение категории", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + " let category\r", + " try {\r", + " category = await api.addCategory(rnd.getCategory());\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + " pm.collectionVariables.set(\"catid\", Number(category.id))\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({\r", + " name : rnd.getCategory().name\r", + " }),\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" + } + }, + { + "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 = JSON.parse(pm.request.body.raw);\r", + "const target = pm.response.json();\r", + "\r", + "pm.test(\"Категория должна содержать поля: id, name\", function () {\r", + "pm.expect(target).to.have.property('id');\r", + "pm.expect(target).to.have.property('name');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(target.id).to.not.be.null;\r", + " pm.expect(source.name).equal(target.name, 'Название категории должно совпадать с отправленным');\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [], + "body": { + "mode": "raw", + "raw": "{{request_body}}" + }, + "url": { + "raw": "{{baseUrl}}/admin/categories/:catId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "categories", + ":catId" + ], + "variable": [ + { + "key": "catId", + "value": "{{catid}}" + } + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Users", + "item": [ + { + "name": "Поиск пользователей", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let compilation;\r", + " try {\r", + " const user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"uid\", user.id)\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", + "\r", + "pm.test(\"Пользователи должны содержать поля: id, name, email\", function () {\r", + " pm.expect(target[0]).to.have.property('id');\r", + " pm.expect(target[0]).to.have.property('name');\r", + " pm.expect(target[0]).to.have.property('email');\r", + "});\r", + "\r", + "pm.test(\"Должен быть найден только один пользователь по заданному фильтру\", function () {\r", + " pm.expect(target.length).to.eql(1);\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(target[0].id).equal(pm.collectionVariables.get(\"uid\"));\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/admin/users?ids={{uid}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "users" + ], + "query": [ + { + "key": "ids", + "value": "{{uid}}", + "description": "id пользователей" + }, + { + "key": "ids", + "value": "-10833646", + "description": "id пользователей", + "disabled": true + }, + { + "key": "from", + "value": "0", + "description": "количество элементов, которые нужно пропустить для формирования текущего набора", + "disabled": true + }, + { + "key": "size", + "value": "10", + "description": "количество элементов в наборе", + "disabled": true + } + ] + } + }, + "response": [] + }, + { + "name": "Добавление нового пользователя", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let user;\r", + " try {\r", + " user = rnd.getUser();\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(user),\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" + } + }, + { + "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, name, email\", function () {\r", + "pm.expect(target).to.have.property('id');\r", + "pm.expect(target).to.have.property('name');\r", + "pm.expect(target).to.have.property('email');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(target.id).to.not.be.null;\r", + " pm.expect(source.name).equal(target.name, 'Имя пользователя должно соответствовать отправленному в запросе');\r", + " pm.expect(source.email).equal(target.email, 'Почта пользователя должна соответствовать отправленной в запросе');\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/users", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "users" + ] + } + }, + "response": [] + }, + { + "name": "Удаление пользователя", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 204\", function () {\r", + " pm.response.to.have.status(204);\r", + "});\r", + "const source = pm.collectionVariables.get('response');\r", + "const userId = pm.collectionVariables.get('uid');\r", + "\r", + "pm.test(\"Пользователь должен быть найден до выполнения запроса\", function(){\r", + " pm.expect(source.length).to.eql(1);\r", + " pm.expect(source[0].id).to.eql(userId);\r", + "});\r", + "let body\r", + "const req = {\r", + " url: \"http://localhost:8080/admin/users?ids=\" + pm.collectionVariables.get(\"uid\"),\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", + " pm.expect(response.json().length).to.eql(0);\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", + " let compilation;\r", + " try {\r", + " const user = await api.addUser(rnd.getUser());\r", + " const foundedUser = await api.findUser(user.id);\r", + " pm.collectionVariables.set(\"uid\", user.id);\r", + " pm.collectionVariables.set(\"response\", foundedUser)\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": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/admin/users/:userId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "users", + ":userId" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id пользователя" + } + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Event", + "item": [ + { + "name": "Добавление нового события", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let event;\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", + " event = rnd.getEvent(category.id);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(event),\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" + } + }, + { + "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, title, annotation, category, paid, eventDate, initiator, description, participantLimit, state, createdOn, location, requestModeration\", function () {\r", + "pm.expect(target).to.have.property('id');\r", + "pm.expect(target).to.have.property('title');\r", + "pm.expect(target).to.have.property('annotation');\r", + "pm.expect(target).to.have.property('category');\r", + "pm.expect(target).to.have.property('paid');\r", + "pm.expect(target).to.have.property('eventDate');\r", + "pm.expect(target).to.have.property('initiator');\r", + "pm.expect(target).to.have.property('description');\r", + "pm.expect(target).to.have.property('participantLimit');\r", + "pm.expect(target).to.have.property('state');\r", + "pm.expect(target).to.have.property('createdOn');\r", + "pm.expect(target).to.have.property('location');\r", + "pm.expect(target).to.have.property('requestModeration');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(target.id).to.not.be.null;\r", + " pm.expect(target.title).equal(source.title, 'Название события должно соответствовать названию события в запросе');\r", + " pm.expect(target.annotation).equal(source.annotation, 'Аннотация события должна соответствовать аннотации события в запросе');\r", + " pm.expect(target.paid.toString()).equal(source.paid.toString(), 'Стоимость события должна соответствовать стоимости события в запросе');\r", + " pm.expect(target.eventDate).equal(source.eventDate, 'Дата проведения события должна соответствовать дате проведения события в запросе');\r", + " pm.expect(target.description).equal(source.description, 'Описание события должно соответствовать описание события в запросе');\r", + " pm.expect(target.participantLimit.toString()).equal(source.participantLimit.toString(), 'Лимит участников события должно соответствовать лимиту участников события в запросе');\r", + " pm.expect(target.location.lat.toString()).equal(source.location.lat.toString(), 'Широта локации проведения события должна соответствовать широте локации проведения события в запросе');\r", + " pm.expect(target.location.lon.toString()).equal(source.location.lon.toString(), 'Долгота локации проведения события должна соответствовать долготе локации проведения события в запросе');\r", + " pm.expect(target.requestModeration.toString()).equal(source.requestModeration.toString(), 'Необходимость модерации события должна соответствовать необходимости модерации события в запросе');\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/events", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + } + ] + }, + "description": "Обратите внимание: дата и время на которые намечено событие не может быть раньше, чем через два часа от текущего момента" + }, + "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", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " let eventBody = rnd.getEvent(category.id);\r", + " eventBody['requestModeration'] = true\r", + " let event = await api.addEvent(user.id, eventBody);\r", + " event = await api.publishEvent(event.id);\r", + " const submittedUser = await api.addUser(rnd.getUser());\r", + " pm.request.removeQueryParams(['eventId']);\r", + " pm.request.addQueryParams([`eventId=` + event.id]);\r", + " pm.collectionVariables.set('uid', submittedUser.id);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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(\"Ответ должен содержать код статуса 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 target = pm.response.json();\r", + "var query = {};\r", + "pm.request.url.query.all().forEach((param) => { query[param.key] = param.value});\r", + "\r", + "pm.test(\"Запрос на участие должен содержать поля: id, requester, event, status, created\", function () {\r", + "pm.expect(target).to.have.property('id');\r", + "pm.expect(target).to.have.property('requester');\r", + "pm.expect(target).to.have.property('event');\r", + "pm.expect(target).to.have.property('status');\r", + "pm.expect(target).to.have.property('created');\r", + "});\r", + "\r", + "pm.test(\"При создании у запроса на участие должен быть статус PENDING\", function () {\r", + " pm.expect(target.status).equal(\"PENDING\");\r", + "});\r", + "\r", + "pm.test(\"Id ивента в запросе и в ответе должны совпадать\", function () {\r", + " pm.expect(target.event.toString()).equal(query['eventId'].toString());\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/users/:userId/requests?eventId=0", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "requests" + ], + "query": [ + { + "key": "eventId", + "value": "0", + "description": "(Required) id события" + } + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + } + ] + }, + "description": "Обратите внимание:\n- нельзя добавить повторный запрос\n- инициатор события не может добавить запрос на участие в своём событии\n- нельзя участвовать в неопубликованном событии\n- если у события достигнут лимит запросов на участие - необходимо вернуть ошибку\n- если для события отключена пре-модерация запросов на участие, то запрос должен автоматически перейти в состояние подтвержденного" + }, + "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", + " const user = await api.addUser(rnd.getUser());\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.request.removeQueryParams(['users', 'categories']);\r", + " pm.request.addQueryParams([`users=` + user.id, 'categories=' + category.id]);\r", + " pm.collectionVariables.set('response', event);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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 source = pm.collectionVariables.get('response');\r", + "const target = pm.response.json()[0];\r", + "\r", + "pm.test(\"Событие должно содержать поля: id, title, annotation, category, paid, eventDate, initiator, views, confirmedRequests, description, participantLimit, state, createdOn, publishedOn, location, requestModeration\", function () {\r", + "pm.expect(target).to.have.property('id');\r", + "pm.expect(target).to.have.property('title');\r", + "pm.expect(target).to.have.property('annotation');\r", + "pm.expect(target).to.have.property('category');\r", + "pm.expect(target).to.have.property('paid');\r", + "pm.expect(target).to.have.property('eventDate');\r", + "pm.expect(target).to.have.property('initiator');\r", + "pm.expect(target).to.have.property('views');\r", + "pm.expect(target).to.have.property('confirmedRequests');\r", + "pm.expect(target).to.have.property('description');\r", + "pm.expect(target).to.have.property('participantLimit');\r", + "pm.expect(target).to.have.property('state');\r", + "pm.expect(target).to.have.property('createdOn');\r", + "pm.expect(target).to.have.property('publishedOn');\r", + "pm.expect(target).to.have.property('location');\r", + "pm.expect(target).to.have.property('requestModeration');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(source.annotation).equal(target.annotation, 'Аннотация события должна соответствовать искомому событию');\r", + " pm.expect(source.category.id).equal(target.category.id, 'Идентификатор категории должен соответствовать искомой категории');\r", + " pm.expect(source.paid.toString()).equal(target.paid.toString(), 'Стоимость посещения события должна соответствовать искомому событию');\r", + " pm.expect(source.eventDate).equal(target.eventDate, 'Дата проведения события должна соответствовать дате искомого события');\r", + " pm.expect(source.description).equal(target.description, 'Описание события должно соответствовать искомому событию');\r", + " pm.expect(source.title).equal(target.title, 'Название события должно соответствовать искомому событию');\r", + " pm.expect(source.participantLimit.toString()).equal(target.participantLimit.toString(), 'Число участников события должно соответствовать искомому событию');\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/admin/events?users=0&states=PUBLISHED&categories=0&rangeStart=2022-01-06%2013%3A30%3A38&rangeEnd=2097-09-06%2013%3A30%3A38&from=0&size=1000", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "events" + ], + "query": [ + { + "key": "users", + "value": "0", + "description": "список id пользователей, чьи события нужно найти" + }, + { + "key": "states", + "value": "PUBLISHED", + "description": "список состояний в которых находятся искомые события" + }, + { + "key": "categories", + "value": "0", + "description": "список id категорий в которых будет вестись поиск" + }, + { + "key": "rangeStart", + "value": "2022-01-06%2013%3A30%3A38", + "description": "дата и время не раньше которых должно произойти событие" + }, + { + "key": "rangeEnd", + "value": "2097-09-06%2013%3A30%3A38", + "description": "дата и время не позже которых должно произойти событие" + }, + { + "key": "from", + "value": "0", + "description": "количество событий, которые нужно пропустить для формирования текущего набора" + }, + { + "key": "size", + "value": "1000", + "description": "количество событий в наборе" + } + ] + }, + "description": "Эндпоинт возвращает полную информацию обо всех событиях подходящих под переданные условия" + }, + "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", + " const user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"uid\", user.id)\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " const event = await api.addEvent(user.id, rnd.getEvent(category.id));\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()[0];\r", + "\r", + "pm.test(\"Событие должно содержать поля: id, title, annotation, category, paid, eventDate\", function () {\r", + " pm.expect(target).to.contain.keys('id', 'title', 'annotation', 'category', 'paid', 'eventDate');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(target.id).to.not.be.null;\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/users/:userId/events?from=0&size=1000", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events" + ], + "query": [ + { + "key": "from", + "value": "0", + "description": "количество элементов, которые нужно пропустить для формирования текущего набора" + }, + { + "key": "size", + "value": "1000", + "description": "количество элементов в наборе" + } + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + } + ] + } + }, + "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", + " const user = await api.addUser(rnd.getUser());\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.request.removeQueryParams(['text', 'categories', 'paid']);\r", + " pm.request.addQueryParams([`text=` + event.annotation, 'categories=' + category.id, 'paid=' + event.paid]);\r", + " pm.collectionVariables.set('response', event);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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 source = pm.collectionVariables.get('response');\r", + "const target = pm.response.json()[0];\r", + "\r", + "pm.test(\"Событие должно содержать поля: id, title, annotation, category, paid, eventDate, initiator, views, confirmedRequests\", function () {\r", + "pm.expect(target).to.have.property('id');\r", + "pm.expect(target).to.have.property('title');\r", + "pm.expect(target).to.have.property('annotation');\r", + "pm.expect(target).to.have.property('category');\r", + "pm.expect(target).to.have.property('paid');\r", + "pm.expect(target).to.have.property('eventDate');\r", + "pm.expect(target).to.have.property('initiator');\r", + "pm.expect(target).to.have.property('views');\r", + "pm.expect(target).to.have.property('confirmedRequests');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(source.annotation).equal(target.annotation, 'Аннотация события должна соответствовать аннотации события с указанным идентификатором');\r", + " pm.expect(source.category.id).equal(target.category.id, 'Категория события должна соответствовать категории события с указанным идентификатором');\r", + " pm.expect(source.paid.toString()).equal(target.paid.toString(), 'Стоимость события должна соответствовать стоимости события с указанным идентификатором');\r", + " pm.expect(source.eventDate).equal(target.eventDate, 'Дата проведения события должна соответствовать дате проведения события с указанным идентификатором');\r", + " pm.expect(source.title).equal(target.title, 'Название события должно соответствовать названию события с указанным идентификатором');\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/events?text=0&categories=0&paid=true&rangeStart=2022-01-06%2013%3A30%3A38&rangeEnd=2097-09-06%2013%3A30%3A38&onlyAvailable=false&sort=EVENT_DATE&from=0&size=1000", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "events" + ], + "query": [ + { + "key": "text", + "value": "0", + "description": "текст для поиска в содержимом аннотации и подробном описании события" + }, + { + "key": "categories", + "value": "0", + "description": "список идентификаторов категорий в которых будет вестись поиск" + }, + { + "key": "paid", + "value": "true", + "description": "поиск только платных/бесплатных событий" + }, + { + "key": "rangeStart", + "value": "2022-01-06%2013%3A30%3A38", + "description": "дата и время не раньше которых должно произойти событие" + }, + { + "key": "rangeEnd", + "value": "2097-09-06%2013%3A30%3A38", + "description": "дата и время не позже которых должно произойти событие" + }, + { + "key": "onlyAvailable", + "value": "false", + "description": "только события у которых не исчерпан лимит запросов на участие" + }, + { + "key": "sort", + "value": "EVENT_DATE", + "description": "Вариант сортировки: по дате события или по количеству просмотров" + }, + { + "key": "from", + "value": "0", + "description": "количество событий, которые нужно пропустить для формирования текущего набора" + }, + { + "key": "size", + "value": "1000", + "description": "количество событий в наборе" + } + ] + }, + "description": "Обратите внимание: \n- это публичный эндпоинт, соответственно в выдаче должны быть только опубликованные события\n- текстовый поиск (по аннотации и подробному описанию) должен быть без учета регистра букв\n- если в запросе не указан диапазон дат [rangeStart-rangeEnd], то нужно выгружать события, которые произойдут позже текущей даты и времени\n- информация о каждом событии должна включать в себя количество просмотров и количество уже одобренных заявок на участие\n- информацию о том, что по этому эндпоинту был осуществлен и обработан запрос, нужно сохранить в сервисе статистики" + }, + "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", + " const user = await api.addUser(rnd.getUser());\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", + " pm.collectionVariables.set('response', event);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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 source = pm.collectionVariables.get('response');\r", + "const target = pm.response.json();\r", + "\r", + "pm.test(\"Событие должно содержать поля: id, title, annotation, category, paid, eventDate, initiator, views, confirmedRequests, description, participantLimit, state, createdOn, publishedOn, location, requestModeration\", function () {\r", + "pm.expect(target).to.have.property('id');\r", + "pm.expect(target).to.have.property('title');\r", + "pm.expect(target).to.have.property('annotation');\r", + "pm.expect(target).to.have.property('category');\r", + "pm.expect(target).to.have.property('paid');\r", + "pm.expect(target).to.have.property('eventDate');\r", + "pm.expect(target).to.have.property('initiator');\r", + "pm.expect(target).to.have.property('views');\r", + "pm.expect(target).to.have.property('confirmedRequests');\r", + "pm.expect(target).to.have.property('description');\r", + "pm.expect(target).to.have.property('participantLimit');\r", + "pm.expect(target).to.have.property('state');\r", + "pm.expect(target).to.have.property('createdOn');\r", + "pm.expect(target).to.have.property('publishedOn');\r", + "pm.expect(target).to.have.property('location');\r", + "pm.expect(target).to.have.property('requestModeration');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(source.annotation).equal(target.annotation, 'Аннотация события должна соответствовать аннотации события с указанным идентификатором');\r", + " pm.expect(source.category.id).equal(target.category.id, 'Категория события должна соответствовать категории события с указанным идентификатором');\r", + " pm.expect(source.paid.toString()).equal(target.paid.toString(), 'Стоимость события должна соответствовать стоимости события с указанным идентификатором');\r", + " pm.expect(source.eventDate).equal(target.eventDate, 'Дата проведения события должна соответствовать дате проведения события с указанным идентификатором');\r", + " pm.expect(source.description).equal(target.description, 'Описание события должно соответствовать описанию события с указанным идентификатором');\r", + " pm.expect(source.title).equal(target.title, 'Название события должно соответствовать названию события с указанным идентификатором');\r", + " pm.expect(source.participantLimit.toString()).equal(target.participantLimit.toString(), 'Лимит участников события должен соответствовать лимиту участников события с указанным идентификатором');\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/events/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "events", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "{{eid}}", + "description": "(Required) id события" + } + ] + }, + "description": "Обратите внимание:\n- событие должно быть опубликовано\n- информация о событии должна включать в себя количество просмотров и количество подтвержденных запросов\n- информацию о том, что по этому эндпоинту был осуществлен и обработан запрос, нужно сохранить в сервисе статистики" + }, + "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", + " const user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"uid\", user.id)\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " const event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " pm.collectionVariables.set(\"eid\", event.id)\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", + "\r", + "pm.test(\"Событие должно содержать поля: id, title, annotation, category, paid, eventDate, initiator, description, participantLimit, state, createdOn, location, requestModeration\", function () {\r", + "pm.expect(target).to.have.property('id');\r", + "pm.expect(target).to.have.property('title');\r", + "pm.expect(target).to.have.property('annotation');\r", + "pm.expect(target).to.have.property('category');\r", + "pm.expect(target).to.have.property('paid');\r", + "pm.expect(target).to.have.property('eventDate');\r", + "pm.expect(target).to.have.property('initiator');\r", + "pm.expect(target).to.have.property('description');\r", + "pm.expect(target).to.have.property('participantLimit');\r", + "pm.expect(target).to.have.property('state');\r", + "pm.expect(target).to.have.property('createdOn');\r", + "pm.expect(target).to.have.property('location');\r", + "pm.expect(target).to.have.property('requestModeration');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(target.id).to.not.be.null;\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/users/:userId/events/:eventId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events", + ":eventId" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + }, + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id события" + } + ] + } + }, + "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", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " let eventBody = rnd.getEvent(category.id);\r", + " eventBody['requestModeration'] = true\r", + " let event = await api.addEvent(user.id, eventBody);\r", + " event = await api.publishEvent(event.id);\r", + " const submittedUser = await api.addUser(rnd.getUser());\r", + " const requestToJoin = await api.publishParticipationRequest(event.id, submittedUser.id);\r", + " pm.collectionVariables.set('response', requestToJoin);\r", + " pm.collectionVariables.set('uid', submittedUser.id);\r", + "\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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 source = pm.collectionVariables.get('response');\r", + "const target = pm.response.json()[0];\r", + "\r", + "pm.test(\"Запрос на участие должен содержать поля: id, requester, event, status, created\", function () {\r", + "pm.expect(target).to.have.property('id');\r", + "pm.expect(target).to.have.property('requester');\r", + "pm.expect(target).to.have.property('event');\r", + "pm.expect(target).to.have.property('status');\r", + "pm.expect(target).to.have.property('created');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(source.id).equal(target.id, 'Идентификатор запроса на участие в событии должен соответствовать идентификатору запроса, созданного ранее');\r", + " pm.expect(source.requester).equal(target.requester, 'Пользователя, запрашивающий доступ на участие в событии, должен соответствовать указанному пользователю');\r", + " pm.expect(source.event).equal(target.event, 'Событие, доступ к которому запрашивает пользователь, должно соответствовать событию, доступ к которому пользователь запрашивал доступ ранее');\r", + " pm.expect(source.created).equal(target.created, 'Время создания запроса на участие в событии должно соответствовать времени создания запроса, созданного ранее указанным пользователем');\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/users/:userId/requests", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "requests" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + } + ] + } + }, + "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", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " let eventBody = rnd.getEvent(category.id);\r", + " eventBody['requestModeration'] = true\r", + " let event = await api.addEvent(user.id, eventBody);\r", + " event = await api.publishEvent(event.id);\r", + " const submittedUser = await api.addUser(rnd.getUser());\r", + " const requestToJoin = await api.publishParticipationRequest(event.id, submittedUser.id);\r", + " pm.collectionVariables.set('response', requestToJoin);\r", + " pm.collectionVariables.set('uid', user.id);\r", + " pm.collectionVariables.set('eid', event.id);\r", + "\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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 source = pm.collectionVariables.get('response');\r", + "const target = pm.response.json()[0];\r", + "\r", + "pm.test(\"Запрос на участие должен содержать поля: id, requester, event, status, created\", function () {\r", + "pm.expect(target).to.have.property('id');\r", + "pm.expect(target).to.have.property('requester');\r", + "pm.expect(target).to.have.property('event');\r", + "pm.expect(target).to.have.property('status');\r", + "pm.expect(target).to.have.property('created');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(source.id).equal(target.id, 'Идентификатор запроса на участие в событии должен соответствовать идентификатору запроса на участие в событии указанного пользователя');\r", + " pm.expect(source.requester).equal(target.requester, 'Автор запроса на участие в событии должен соответствовать указанному пользователю');\r", + " pm.expect(source.event).equal(target.event, 'Событие в ответе должно соответствовать событию с запросом на участие от указанного пользователя');\r", + " pm.expect(source.created).equal(target.created, 'Время создания запроса на участие в событии должно соответствовать времени создания запроса на участие в событии указанного пользователя');\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/users/:userId/events/:eventId/requests", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events", + ":eventId", + "requests" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + }, + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id события" + } + ] + } + }, + "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", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " let event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " let event2 = rnd.getEvent(category.id)\r", + " event2.stateAction = \"PUBLISH_EVENT\"\r", + " pm.collectionVariables.set('response', event2);\r", + " pm.collectionVariables.set(\"eid\", event.id)\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: event2,\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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 source = pm.collectionVariables.get('response');\r", + "const target = pm.response.json();\r", + "\r", + "pm.test(\"Событие должно содержать поля: id, title, annotation, category, paid, eventDate, initiator, description, participantLimit, state, createdOn, publishedOn, location, requestModeration\", function () {\r", + "pm.expect(target).to.have.property('id');\r", + "pm.expect(target).to.have.property('title');\r", + "pm.expect(target).to.have.property('annotation');\r", + "pm.expect(target).to.have.property('category');\r", + "pm.expect(target).to.have.property('paid');\r", + "pm.expect(target).to.have.property('eventDate');\r", + "pm.expect(target).to.have.property('initiator');\r", + "pm.expect(target).to.have.property('description');\r", + "pm.expect(target).to.have.property('participantLimit');\r", + "pm.expect(target).to.have.property('state');\r", + "pm.expect(target).to.have.property('createdOn');\r", + "pm.expect(target).to.have.property('publishedOn');\r", + "pm.expect(target).to.have.property('location');\r", + "pm.expect(target).to.have.property('requestModeration');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(source.annotation).equal(target.annotation, 'Аннотация события должна соответствовать искомому событию');\r", + " pm.expect(source.paid.toString()).equal(target.paid.toString(), 'Стоимость события должна соответствовать искомому событию');\r", + " pm.expect(source.eventDate).equal(target.eventDate, 'Дата проведения события должна соответствовать искомому событию');\r", + " pm.expect(source.description).equal(target.description, 'Описание события должно соответствовать искомому событию');\r", + " pm.expect(source.title).equal(target.title, 'Название события должно соответствовать искомому событию');\r", + " pm.expect(source.participantLimit.toString()).equal(target.participantLimit.toString(), 'Лимит участников события должен соответствовать искомому событию');\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}" + }, + "url": { + "raw": "{{baseUrl}}/admin/events/:eventId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "events", + ":eventId" + ], + "variable": [ + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id события" + } + ] + }, + "description": "Обратите внимание:\n - дата начала события должна быть не ранее чем за час от даты публикации.\n- событие должно быть в состоянии ожидания публикации" + }, + "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", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " const event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " pm.collectionVariables.set(\"uid\", user.id);\r", + " pm.collectionVariables.set(\"eid\", event.id);\r", + " pm.collectionVariables.set(\"response\", event);\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({\r", + " stateAction: \"CANCEL_REVIEW\"\r", + " }),\r", + " options: { raw: { language: 'json' } }\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 source = pm.collectionVariables.get(\"response\");\r", + "const target = pm.response.json();\r", + "\r", + "\r", + "pm.test(\"Событие должно содержать поля: id, title, annotation, category, paid, eventDate, initiator, description, participantLimit, state, createdOn, location, requestModeration\", function () {\r", + "pm.expect(target).to.have.property('id');\r", + "pm.expect(target).to.have.property('title');\r", + "pm.expect(target).to.have.property('annotation');\r", + "pm.expect(target).to.have.property('category');\r", + "pm.expect(target).to.have.property('paid');\r", + "pm.expect(target).to.have.property('eventDate');\r", + "pm.expect(target).to.have.property('initiator');\r", + "pm.expect(target).to.have.property('description');\r", + "pm.expect(target).to.have.property('participantLimit');\r", + "pm.expect(target).to.have.property('state');\r", + "pm.expect(target).to.have.property('createdOn');\r", + "pm.expect(target).to.have.property('location');\r", + "pm.expect(target).to.have.property('requestModeration');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(source.annotation).equal(target.annotation, 'Аннотация отменённого события должна соответствовать аннотации события до отмены');\r", + " pm.expect(source.category.id).equal(target.category.id, 'Категория отменённого события должна соответствовать категории события до отмены');\r", + " pm.expect(source.paid.toString()).equal(target.paid.toString(), 'Стоимость отменённого события должна соответствовать стоимости события до отмены');\r", + " pm.expect(source.eventDate).equal(target.eventDate, 'Дата проведения отменённого события должна соответствовать дате проведения события до отмены');\r", + " pm.expect(source.description).equal(target.description, 'Описание отменённого события должно соответствовать описанию события до отмены');\r", + " pm.expect(source.title).equal(target.title, 'Название отменённого события должно соответствовать названию события до отмены');\r", + " pm.expect(source.participantLimit.toString()).equal(target.participantLimit.toString(), 'Лимит участников отменённого события должен соответствовать лимиту участников события до отмены');\r", + " pm.expect(source.paid.toString()).equal(target.paid.toString(), 'Стоимость отменённого события должна соответствовать стоимости события до отмены');\r", + "});\r", + "\r", + "pm.test(\"Событие должно иметь статус PENDING при создании и статус CANCELED после выполнения запроса\", function () {\r", + " pm.expect(source.state).equal(\"PENDING\");\r", + " pm.expect(target.state).equal(\"CANCELED\");\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/events/:eventId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events", + ":eventId" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + }, + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id отменяемого события" + } + ] + }, + "description": "Обратите внимание: Отменить можно только событие в состоянии ожидания модерации." + }, + "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", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " let eventBody = rnd.getEvent(category.id);\r", + " eventBody['requestModeration'] = true\r", + " let event = await api.addEvent(user.id, eventBody);\r", + " event = await api.publishEvent(event.id);\r", + " const submittedUser = await api.addUser(rnd.getUser());\r", + " const requestToJoin = await api.publishParticipationRequest(event.id, submittedUser.id);\r", + " pm.collectionVariables.set('response', requestToJoin);\r", + " pm.collectionVariables.set('uid', submittedUser.id);\r", + " pm.collectionVariables.set('reqid', requestToJoin.id);\r", + "\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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('response');\r", + "\r", + "pm.test(\"Запрос на участие должен содержать поля: id, requester, event, status, created\", function () {\r", + "pm.expect(target).to.have.property('id');\r", + "pm.expect(target).to.have.property('requester');\r", + "pm.expect(target).to.have.property('event');\r", + "pm.expect(target).to.have.property('status');\r", + "pm.expect(target).to.have.property('created');\r", + "});\r", + "\r", + "pm.test(\"При создании у запроса на участие должен быть статус PENDING, а при удалении - CANCELED\", function () {\r", + " pm.expect(source.status).equal(\"PENDING\");\r", + " pm.expect(target.status).equal(\"CANCELED\");\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(source.id).equal(target.id, 'Идентификатор отменённого запроса на участие в событии должен соответствовать идентификатору запроса до отмены');\r", + " pm.expect(source.requester).equal(target.requester, 'Пользователь, отменяющий запрос на участие в событии, должен соответствовать текущему пользователю');\r", + " pm.expect(source.event).equal(target.event, 'Событие отменённого запроса на участие должно соответствовать запросу на участие в событии до отмены');\r", + " pm.expect(source.created).equal(target.created, 'Дата создания отменённого запроса на участие в событии должна соответствовать дате создания запроса до отмены');\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/users/:userId/requests/:requestId/cancel", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "requests", + ":requestId", + "cancel" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + }, + { + "key": "requestId", + "value": "{{reqid}}", + "description": "(Required) id запроса на участие" + } + ] + } + }, + "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", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " let eventBody = rnd.getEvent(category.id);\r", + " eventBody['requestModeration'] = true\r", + " let event = await api.addEvent(user.id, eventBody);\r", + " event = await api.publishEvent(event.id);\r", + " const submittedUser = await api.addUser(rnd.getUser());\r", + " const requestToJoin = await api.publishParticipationRequest(event.id, submittedUser.id);\r", + " pm.collectionVariables.set('response', requestToJoin);\r", + " pm.collectionVariables.set('uid', user.id);\r", + " pm.collectionVariables.set('eid', event.id);\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({requestIds: [requestToJoin.id],\r", + " status:\"REJECTED\"}),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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 source = pm.collectionVariables.get('response');\r", + "const target = pm.response.json()[\"rejectedRequests\"][0];\r", + "\r", + "pm.test(\"Запрос на участие должен содержать поля: id, requester, event, status, created\", function () {\r", + "pm.expect(target).to.have.property('id');\r", + "pm.expect(target).to.have.property('requester');\r", + "pm.expect(target).to.have.property('event');\r", + "pm.expect(target).to.have.property('status');\r", + "pm.expect(target).to.have.property('created');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(source.id).equal(target.id, 'Идентификатор запроса на участие в событии должен соответствовать идентификатору запроса на участие в событии указанного пользователя');\r", + " pm.expect(source.requester).equal(target.requester, 'Пользователь, запрашивающий доступ на участие в событии должен пользователю, отправившему запрос на участие в событии указанного пользователя ранее');\r", + " pm.expect(source.event).equal(target.event, 'Событие, запрос на участие в котором надо подтвердить, должно соответствовать событию указанного пользователя');\r", + " pm.expect(source.created).equal(target.created, 'Время создания запроса на участие в событии после подтверждения должно соответствовать времени создания запроса на участие в событии указанного пользователя до подтверждения');\r", + "});\r", + "\r", + "pm.test(\"Запрос на участие должен иметь статус PENDING при создании и статус REJECTED после выполнения запроса\", function () {\r", + " pm.expect(source.status).equal(\"PENDING\");\r", + " pm.expect(target.status).equal(\"REJECTED\");\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"requestIds\": [\n 1,\n 2,\n 3\n ],\n \"status\": \"CONFIRMED\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/events/:eventId/requests", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events", + ":eventId", + "requests" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + }, + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id события текущего пользователя" + } + ] + }, + "description": "Обратите внимание:\n- если для события лимит заявок равен 0 или отключена пре-модерация заявок, то подтверждение заявок не требуется\n- нельзя подтвердить заявку, если уже достигнут лимит по заявкам на данное событие\n- статус можно изменить только у заявок, находящихся в состоянии ожидания\n- если при подтверждении данной заявки, лимит заявок для события исчерпан, то все неподтверждённые заявки необходимо отклонить" + }, + "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", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " let eventBody = rnd.getEvent(category.id);\r", + " eventBody['requestModeration'] = true\r", + " let event = await api.addEvent(user.id, eventBody);\r", + " event = await api.publishEvent(event.id);\r", + " const submittedUser = await api.addUser(rnd.getUser());\r", + " const requestToJoin = await api.publishParticipationRequest(event.id, submittedUser.id);\r", + " pm.collectionVariables.set('response', requestToJoin);\r", + " pm.collectionVariables.set('uid', user.id);\r", + " pm.collectionVariables.set('eid', event.id);\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({requestIds: [requestToJoin.id],\r", + " status:\"CONFIRMED\"}),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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 source = pm.collectionVariables.get('response');\r", + "const target = pm.response.json()[\"confirmedRequests\"][0];\r", + "\r", + "pm.test(\"Запрос на участие должен содержать поля: id, requester, event, status, created\", function () {\r", + "pm.expect(target).to.have.property('id');\r", + "pm.expect(target).to.have.property('requester');\r", + "pm.expect(target).to.have.property('event');\r", + "pm.expect(target).to.have.property('status');\r", + "pm.expect(target).to.have.property('created');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(source.id).equal(target.id, 'Идентификатор запроса на участие в событии должен соответствовать идентификатору запроса на участие в событии указанного пользователя');\r", + " pm.expect(source.requester).equal(target.requester, 'Пользователь, запрашивающий доступ на участие в событии должен пользователю, отправившему запрос на участие в событии указанного пользователя ранее');\r", + " pm.expect(source.event).equal(target.event, 'Событие, запрос на участие в котором надо подтвердить, должно соответствовать событию указанного пользователя');\r", + " pm.expect(source.created).equal(target.created, 'Время создания запроса на участие в событии после подтверждения должно соответствовать времени создания запроса на участие в событии указанного пользователя до подтверждения');\r", + "});\r", + "\r", + "pm.test(\"Запрос на участие должен иметь статус PENDING при создании и статус CONFIRMED после выполнения запроса\", function () {\r", + " pm.expect(source.status).equal(\"PENDING\");\r", + " pm.expect(target.status).equal(\"CONFIRMED\");\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/events/:eventId/requests", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events", + ":eventId", + "requests" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + }, + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id события текущего пользователя" + } + ] + }, + "description": "Обратите внимание:\n- если для события лимит заявок равен 0 или отключена пре-модерация заявок, то подтверждение заявок не требуется\n- нельзя подтвердить заявку, если уже достигнут лимит по заявкам на данное событие\n- статус можно изменить только у заявок, находящихся в состоянии ожидания\n- если при подтверждении данной заявки, лимит заявок для события исчерпан, то все неподтверждённые заявки необходимо отклонить" + }, + "response": [] + } + ] + }, + { + "name": "Compilation", + "item": [ + { + "name": "Добавление новой подборки", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let compilation;\r", + " try {\r", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " const event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " compilation = rnd.getCompilation(event.id);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(compilation),\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" + } + }, + { + "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, title, pinned, events\", function () {\r", + "pm.expect(target).to.have.property('id');\r", + "pm.expect(target).to.have.property('title');\r", + "pm.expect(target).to.have.property('pinned');\r", + "pm.expect(target).to.have.property('events');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(target.id).to.not.be.null;\r", + " pm.expect(target.title).to.be.a(\"string\");\r", + " pm.expect(target.events).to.be.an(\"array\");\r", + "\r", + " pm.expect(source.events[0]).equal(target.events[0].id, 'Идентификаторы событий в подборке должен быть идентичен идентификаторам, указанным при создании подборки ');\r", + " pm.expect(source.title).equal(target.title, 'Название подборки должно соответствовать указанному при создании');\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/compilations", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "compilations" + ] + } + }, + "response": [] + }, + { + "name": "Получение подборки событий по её id", + "event": [ + { + "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", + " const category = await api.addCategory(rnd.getCategory());\r", + " const compilation = await api.addCompilation(rnd.getCompilation());\r", + " pm.collectionVariables.set('response', compilation);\r", + " pm.collectionVariables.set('compid', compilation.id);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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 source = pm.collectionVariables.get('response');\r", + "const target = pm.response.json();\r", + "\r", + "pm.test(\"Подборка должны содержать поля: id, title, pinned, events\", function () {\r", + "pm.expect(target).to.have.property('id');\r", + "pm.expect(target).to.have.property('title');\r", + "pm.expect(target).to.have.property('pinned');\r", + "pm.expect(target).to.have.property('events');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(source.id).equal(target.id, 'Идентификатор подборки должен соответствовать идентификатору подборки добавленной ранее');\r", + " pm.expect(source.title).equal(target.title, 'Название подборки должно соответствовать названию подборки добавленной ранее');\r", + " pm.expect(source.pinned).equal(target.pinned, 'Закреплённость подборки должна соответствовать закреплённости подборки добавленной ранее');\r", + " pm.expect(source.events.join()).equal(target.events.join(), 'События подборки должны соответствовать событиям подборки добавленной ранее');\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/compilations/:compId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "compilations", + ":compId" + ], + "variable": [ + { + "key": "compId", + "value": "{{compid}}", + "description": "(Required) id подборки" + } + ] + } + }, + "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", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " let newComp = rnd.getCompilation();\r", + " newComp['pinned'] = true;\r", + " const compilation = await api.addCompilation(newComp);\r", + " pm.collectionVariables.set('response', compilation);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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 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, title, pinned, events\", function () {\r", + "pm.expect(target[0]).to.have.property('id');\r", + "pm.expect(target[0]).to.have.property('title');\r", + "pm.expect(target[0]).to.have.property('pinned');\r", + "pm.expect(target[0]).to.have.property('events');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(source.id).equal(founded.id, 'Идентификатор подборки должен соответствовать идентификатору подборки добавленной ранее');\r", + " pm.expect(source.title).equal(founded.title, 'Название подборки должно соответствовать названию подборки добавленной ранее');\r", + " pm.expect(source.pinned).equal(founded.pinned, 'Закрепленность подборки должна соответствовать закрепленности подборки добавленной ранее');\r", + " pm.expect(source.events.join()).equal(founded.events.join(), 'События подборки должны соответствовать событиям подборки добавленной ранее');\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/compilations?pinned=true&from=0&size=1000", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "compilations" + ], + "query": [ + { + "key": "pinned", + "value": "true", + "description": "искать только закрепленные/не закрепленные подборки" + }, + { + "key": "from", + "value": "0", + "description": "количество элементов, которые нужно пропустить для формирования текущего набора" + }, + { + "key": "size", + "value": "1000", + "description": "количество элементов в наборе" + } + ] + } + }, + "response": [] + }, + { + "name": "Удаление подборки", + "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/compilations?from=0&size=1000\" + pm.collectionVariables.get(\"uid\"),\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('compid')){\r", + " throw new Error(\"Подборка все еще находится в списке существующих\");\r", + " }\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", + " const category = await api.addCategory(rnd.getCategory());\r", + " const compilation = await api.addCompilation(rnd.getCompilation());\r", + " const foundedCompilation = await api.findCompilation(compilation.id);\r", + " pm.collectionVariables.set('compid', compilation.id);\r", + " pm.collectionVariables.set('response', foundedCompilation);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/admin/compilations/:compId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "compilations", + ":compId" + ], + "variable": [ + { + "key": "compId", + "value": "{{compid}}", + "description": "(Required) id подборки" + } + ] + } + }, + "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", + " const user = await api.addUser(rnd.getUser());\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", + " const compilation = await api.addCompilation(rnd.getCompilation());\r", + " const foundedCompilation = await api.findCompilation(compilation.id);\r", + " pm.collectionVariables.set('compid', compilation.id);\r", + " pm.collectionVariables.set('response', foundedCompilation);\r", + " pm.collectionVariables.set('eid', event.id);\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({\r", + " events : [event.id],\r", + " pinned: true,\r", + " title: rnd.getCompilation().name\r", + " }),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\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\", function () {\r", + " pm.response.to.be.ok; \r", + "});\r", + "\r", + "source = pm.collectionVariables.get('response');\r", + "compId = pm.collectionVariables.get('compid');\r", + "eventId = pm.collectionVariables.get('eid');\r", + "\r", + "pm.test(\"Событие не должно быть найдено в подборке до добавления\", function () {\r", + " pm.expect(source.events.length).equal(0);\r", + "});\r", + "\r", + "pm.sendRequest({\r", + " url: pm.collectionVariables.get(\"baseUrl\") + \"/compilations/\" + compId,\r", + " method: 'GET',\r", + " }, (error, response) => {\r", + " \r", + " pm.test(\"Событие должно быть найдено в подборке после добавления\", function () {\r", + " pm.expect(response.json().events[0].id).equal(eventId);\r", + " });\r", + " });" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [], + "body": { + "mode": "raw", + "raw": "{{request_body}}" + }, + "url": { + "raw": "{{baseUrl}}/admin/compilations/:compId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "compilations", + ":compId" + ], + "variable": [ + { + "key": "compId", + "value": "{{compid}}" + } + ] + } + }, + "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 findEvent(eventId, verbose=null) {", + " return this.get('/events/' + eventId, null, \"Ошибка при поиске события по id\", verbose);", + " }", + "", + " async findUser(userId, verbose=null) {", + " return this.get('/admin/users?ids=' + userId, 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());", + " }", + " if (response.stream.length === 0){", + " reject(new Error('Отправлено пустое тело ответа'))", + " }else{", + " 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() * 10000 * Math.random()).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}}'),", + " }", + " }", + "", + " getCompilation(...eventIds) {", + " return {", + " title: pm.variables.replaceIn('{{$randomLoremSentence}}').slice(0, 50),", + " 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');", + " }", + "", + " getWord(length = 1) {", + " let result = '';", + " const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';", + " const charactersLength = characters.length;", + " let counter = 0;", + " while (counter < length) {", + " result += characters.charAt(Math.floor(Math.random() * charactersLength));", + " counter += 1;", + " }", + " return result;", + " }", + "}" + ] + } + }, + { + "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": "confirmedRequests", + "value": "" + }, + { + "key": "responseArr", + "value": "" + }, + { + "key": "source1", + "value": "" + }, + { + "key": "source2", + "value": "" + }, + { + "key": "fromId", + "value": "0" + }, + { + "key": "source", + "value": "" + } + ] +} \ No newline at end of file diff --git a/Postman/ewm-stat-service.json b/Postman/ewm-stat-service.json new file mode 100644 index 0000000..25dca47 --- /dev/null +++ b/Postman/ewm-stat-service.json @@ -0,0 +1,1027 @@ +{ + "info": { + "_postman_id": "1b12f39d-a514-42d9-99a1-2d2f4026b8e8", + "name": "\"Explore with me\" API статистика", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "23073145", + "_collection_link": "https://universal-shadow-295426.postman.co/workspace/My-Workspace~4200f6aa-0504-44b1-8a1d-707d0dcbd5ce/collection/23073145-1b12f39d-a514-42d9-99a1-2d2f4026b8e8?action=share&creator=23073145&source=collection_link" + }, + "item": [ + { + "name": "Сохранение информации о том, что к эндпоинту был запрос", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let post;\r", + " try {\r", + " post = rnd.getPost();\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(post),\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" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 201 и данные в формате json\", function () {\r", + " pm.response.to.have.status(201);\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/hit", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "hit" + ] + }, + "description": "Сохранение информации о том, что на uri конкретного сервиса был отправлен запрос пользователем. Название сервиса, uri и ip пользователя указаны в теле запроса." + }, + "response": [] + }, + { + "name": "Получение статистики по посещениям. Обратите внимание: значение даты и времени нужно закодировать (например используя java.net.URLEncoder.encode)(Тест на /events/{id})", + "event": [ + { + "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", + " 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(\"uri\", '/events/' + event.id);\r", + "\r", + " pm.sendRequest({\r", + " url : \"http://localhost:8080/events/\" + event.id,\r", + " method : \"GET\",\r", + " header: { \"Content-Type\": \"application/json\" }\r", + " }, (error, response) => {\r", + " pm.sendRequest({\r", + " url : \"http://localhost:9090/stats?start=2020-05-05 00:00:00&end=2035-05-05 00:00:00&uris=/events/\" + event.id.toString() + \"&unique=false\",\r", + " method : \"GET\",\r", + " header: { \"Content-Type\": \"application/json\" }\r", + " }, (error, response) => {\r", + " pm.collectionVariables.set('source', response.json());\r", + " pm.sendRequest({\r", + " url : \"http://localhost:8080/events/\" + event.id,\r", + " method : \"GET\",\r", + " header: { \"Content-Type\": \"application/json\" }\r", + " });\r", + " });\r", + " });\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()[0];\r", + "\r", + "pm.test(\"Посты должны содержать поля: app, uri, hits\", function () {\r", + " pm.expect(target).to.have.all.keys('app', 'uri', 'hits');\r", + "});\r", + "\r", + "const source = pm.collectionVariables.get(\"source\")[0];\r", + "\r", + "\r", + "pm.test(\"После выполнения запроса GET /events/{id} должно увеличиться количество хитов.\", function(){\r", + " pm.expect(source.hits + 1).equal(target.hits, 'Количество хитов после выполнения запроса GET /events/{id} должно быть больше на 1.');\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=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 для которых нужно выгрузить статистику" + }, + { + "key": "unique", + "value": "false", + "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", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " let event1 = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " event1 = await api.publishEvent(event1.id);\r", + " let event2 = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " event2 = await api.publishEvent(event2.id);\r", + " pm.collectionVariables.set(\"uri\", '/events/' + event1.id + '&uris=/events/' + event2.id);\r", + "\r", + " pm.sendRequest({\r", + " url : \"http://localhost:8080/events/\" + event1.id,\r", + " method : \"GET\",\r", + " header: { \"Content-Type\": \"application/json\" }\r", + " }, (error, response) => {\r", + " pm.sendRequest({\r", + " url : \"http://localhost:8080/events/\" + event2.id,\r", + " method : \"GET\",\r", + " header: { \"Content-Type\": \"application/json\" }\r", + " }, (error, response) => {\r", + " pm.sendRequest({\r", + " url : \"http://localhost:8080/events/\" + event2.id,\r", + " method : \"GET\",\r", + " header: { \"Content-Type\": \"application/json\" }\r", + " }, (error, response) => {\r", + " });\r", + " });\r", + " });\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", + "\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(target[0].hits).equal(2);\r", + " pm.expect(target[1].hits).equal(1);\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": [] + }, + { + "name": "Получение статистики по посещениям. Обратите внимание: значение даты и времени нужно закодировать (например используя java.net.URLEncoder.encode)(Тест на /events)", + "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", + " \r", + " pm.sendRequest({\r", + " url : \"http://localhost:8080/events\",\r", + " method : \"GET\",\r", + " header: { \"Content-Type\": \"application/json\" }\r", + " }, (error, response) => {\r", + " \r", + " pm.sendRequest({\r", + " url : \"http://localhost:9090/stats?start=2020-05-05 00:00:00&end=2035-05-05 00:00:00&uris=/events&unique=false\",\r", + " method : \"GET\",\r", + " header: { \"Content-Type\": \"application/json\" }\r", + " }, (error, response) => {\r", + " pm.collectionVariables.set('source', response.json());\r", + " pm.sendRequest({\r", + " url : \"http://localhost:8080/events\",\r", + " method : \"GET\",\r", + " header: { \"Content-Type\": \"application/json\" }\r", + " });\r", + " });\r", + " });\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()[0];\r", + "\r", + "pm.test(\"Посты должны содержать поля: app, uri, hits\", function () {\r", + " pm.expect(target).to.have.all.keys('app', 'uri', 'hits');\r", + "});\r", + "\r", + "const source = pm.collectionVariables.get(\"source\")[0];\r", + "\r", + "\r", + "pm.test(\"После выполнения запроса GET /events должно увеличиться количество хитов.\", function(){\r", + " pm.expect(source.hits + 1).equal(target.hits, 'Количество хитов после выполнения запроса GET /events должно быть больше на 1.');\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=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 для которых нужно выгрузить статистику" + }, + { + "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", + " \r", + " pm.sendRequest({\r", + " url : \"http://localhost:8080/events\",\r", + " method : \"GET\",\r", + " header: { \"Content-Type\": \"application/json\" }\r", + " }, (error, response) => {\r", + " pm.sendRequest({\r", + " url : \"http://localhost:8080/events\",\r", + " method : \"GET\",\r", + " header: { \"Content-Type\": \"application/json\" }\r", + " }, (error, response) => {\r", + " \r", + " pm.sendRequest({\r", + " url : \"http://localhost:9090/stats?start=2020-05-05 00:00:00&end=2035-05-05 00:00:00&uris=/events&unique=true\",\r", + " method : \"GET\",\r", + " header: { \"Content-Type\": \"application/json\" }\r", + " }, (error, response) => {\r", + " pm.collectionVariables.set('source', response.json());\r", + " });\r", + " });\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", + "\r", + "pm.test(\"Посты должны содержать поля: app, uri, hits\", function () {\r", + " pm.expect(target).to.have.all.keys('app', 'uri', 'hits');\r", + "});\r", + "\r", + "const source = pm.collectionVariables.get(\"source\")[0];\r", + "\r", + "pm.test(\"Количество уникальных просмотров с одного ip должно равняться 1\", function () {\r", + " pm.expect(source.hits == 1);\r", + "})\r", + "\r", + "pm.test(\"Количество просмотров с одного ip должно быть больше 1\", function () {\r", + " pm.expect(target.hits > 1);\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": [] + }, + { + "name": "Получение статистики по посещениям. (Тест на верную обработку запроса с неверными датами начала и конца диапазона времени)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400\", function () {\r", + " pm.response.to.be.badRequest;\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/stats?start=2035-05-05 00:00:00&end=2020-05-05 00:00:00&uris={{uri}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "stats" + ], + "query": [ + { + "key": "start", + "value": "2035-05-05 00:00:00", + "description": "(Required) Дата и время начала диапазона за который нужно выгрузить статистику (в формате \"yyyy-MM-dd HH:mm:ss\")" + }, + { + "key": "end", + "value": "2020-05-05 00:00:00", + "description": "(Required) Дата и время конца диапазона за который нужно выгрузить статистику (в формате \"yyyy-MM-dd HH:mm:ss\")" + }, + { + "key": "uris", + "value": "{{uri}}", + "description": "Список uri для которых нужно выгрузить статистику" + }, + { + "key": "unique", + "value": "false", + "description": "Нужно ли учитывать только уникальные посещения (только с уникальным ip)", + "disabled": true + } + ] + } + }, + "response": [] + }, + { + "name": "Тест на верную обработку запроса без даты начала", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400\", function () {\r", + " pm.response.to.be.badRequest;\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/stats?end=2020-05-05 00:00:00&uris={{uri}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "stats" + ], + "query": [ + { + "key": "start", + "value": "2035-05-05 00:00:00", + "description": "(Required) Дата и время начала диапазона за который нужно выгрузить статистику (в формате \"yyyy-MM-dd HH:mm:ss\")", + "disabled": true + }, + { + "key": "end", + "value": "2020-05-05 00:00:00", + "description": "(Required) Дата и время конца диапазона за который нужно выгрузить статистику (в формате \"yyyy-MM-dd HH:mm:ss\")" + }, + { + "key": "uris", + "value": "{{uri}}", + "description": "Список uri для которых нужно выгрузить статистику" + }, + { + "key": "unique", + "value": "false", + "description": "Нужно ли учитывать только уникальные посещения (только с уникальным ip)", + "disabled": true + } + ] + } + }, + "response": [] + }, + { + "name": "Тест на верную обработку запроса без даты конца", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400\", function () {\r", + " pm.response.to.be.badRequest;\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/stats?start=2035-05-05 00:00:00&uris={{uri}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "stats" + ], + "query": [ + { + "key": "start", + "value": "2035-05-05 00:00:00", + "description": "(Required) Дата и время начала диапазона за который нужно выгрузить статистику (в формате \"yyyy-MM-dd HH:mm:ss\")" + }, + { + "key": "end", + "value": "2020-05-05 00:00:00", + "description": "(Required) Дата и время конца диапазона за который нужно выгрузить статистику (в формате \"yyyy-MM-dd HH:mm:ss\")", + "disabled": true + }, + { + "key": "uris", + "value": "{{uri}}", + "description": "Список uri для которых нужно выгрузить статистику" + }, + { + "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 addUser(user, verbose=null) {", + " return this.post(\"/admin/users\", user, \"http://localhost:8080\", \"Ошибка при добавлении нового пользователя: \", verbose);", + " }", + "", + " async addCategory(category, verbose=null) {", + " return this.post(\"/admin/categories\", category, \"http://localhost:8080\", \"Ошибка при добавлении новой категории: \", verbose);", + " }", + "", + " async addEvent(userId, event, verbose=null) {", + " return this.post(\"/users/\" + userId + \"/events\", event, \"http://localhost:8080\", \"Ошибка при добавлении нового события: \", verbose);", + " }", + "", + " async publishEvent(eventId, verbose=null) {", + " return this.patch('/admin/events/' + eventId, {stateAction: \"PUBLISH_EVENT\"},\"Ошибка при публикации события\", verbose);", + " }", + "", + " async patch(path, body = null, errorText = \"Ошибка при выполнении patch-запроса: \", verbose=null) {", + " return this.sendRequest(\"PATCH\", path, \"http://localhost:8080\", body, errorText);", + " }", + "", + " async post(path, body, newBaseUrl=null, errorText = \"Ошибка при выполнении post-запроса: \", verbose=null) {", + " return this.sendRequest(\"POST\", path, newBaseUrl, body, errorText);", + " }", + "", + " async sendRequest(method, path, newBaseUrl=null, body=null, errorText = \"Ошибка при выполнении запроса: \", verbose=null) {", + " return new Promise((resolve, reject) => {", + " verbose = verbose == null ? this._verbose : verbose;", + " let request;", + " if (newBaseUrl==null)", + " request = {", + " url: this.baseUrl + path,", + " method: method,", + " body: body == null ? \"\" : JSON.stringify(body),", + " header: { \"Content-Type\": \"application/json\" },", + " };", + " else", + " request = {", + " url: newBaseUrl + 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: pm.variables.replaceIn('{{$randomIP}}'),", + " timestamp: this.getPastDateTime()", + " }", + " }", + "", + " 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}}'),", + " }", + " }", + " ", + " getCompilation(...eventIds) { ", + " return { ", + " title: pm.variables.replaceIn('{{$randomLoremSentence}}'), ", + " pinned: pm.variables.replaceIn('{{$randomBoolean}}'), ", + " events: eventIds ", + " }; ", + " }", + "", + " 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');", + " }", + "", + " 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:9090", + "type": "string" + }, + { + "key": "uri", + "value": "1" + }, + { + "key": "source", + "value": "" + } + ] +} \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index a02f49e..7779ce4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,20 @@ version: '3.1' services: + stats-server: + build: stats-server + image: stats-server + container_name: stats-server + ports: + - "9090:9090" + depends_on: + - stats-db + environment: + - SPRING_DATASOURCE_URL=jdbc:postgresql://stats-db:5432/statdb + - SPRING_DATASOURCE_USERNAME=statdb + - SPRING_DATASOURCE_PASSWORD=statdb + stats-db: - image: postgres:16.1 + image: postgres:14-alpine container_name: postgres-stat ports: - "5432:5432" @@ -15,19 +28,6 @@ services: interval: 5s retries: 10 - stats-server: - build: stats-server - image: stats-server - container_name: stats-server - ports: - - "9090:9090" - depends_on: - - stats-db - environment: - - SPRING_DATASOURCE_URL=jdbc:postgresql://stats-db:5432/statdb - - SPRING_DATASOURCE_USERNAME=statdb - - SPRING_DATASOURCE_PASSWORD=statdb - ewm-service: build: ewm-service image: ewm-service @@ -35,22 +35,24 @@ services: ports: - "8080:8080" depends_on: - - stats-server + - ewm-db environment: + - SPRING_DATASOURCE_URL=jdbc:postgresql://ewm-db:5432/ewmdb + - SPRING_DATASOURCE_USERNAME=ewmdb + - SPRING_DATASOURCE_PASSWORD=ewmdb - STATSERVER_URL=http://stats-server:9090 ewm-db: - image: postgres:16.1 + image: postgres:14-alpine container_name: postgres-ewm ports: - - "5434:5434" + - "5434:5432" environment: - - POSTGRES_PASSWORD=statewm - - POSTGRES_USER=statewm - - POSTGRES_DB=statewm + - POSTGRES_PASSWORD=ewmdb + - POSTGRES_USER=ewmdb + - POSTGRES_DB=ewmdb healthcheck: test: pg_isready -q -d $$POSTGRES_DB -U $$POSTGRES_USER timeout: 5s interval: 5s retries: 10 - diff --git a/ewm-service/README.md b/ewm-service/README.md index f6eea04..0eb2ec3 100644 --- a/ewm-service/README.md +++ b/ewm-service/README.md @@ -1 +1,6 @@ -# ewm-service \ No newline at end of file +# ewm-service +Сервис событий + +Схема базы данных. +![схема базы данных](/schema.png) + diff --git a/ewm-service/pom.xml b/ewm-service/pom.xml index 9eb9b4f..0cadd0b 100644 --- a/ewm-service/pom.xml +++ b/ewm-service/pom.xml @@ -14,17 +14,17 @@ org.springframework.boot - spring-boot-starter-web + spring-boot-starter-actuator org.springframework.boot - spring-boot-starter-validation + spring-boot-starter-web org.springframework.boot - spring-boot-starter-actuator + spring-boot-starter-validation @@ -51,6 +51,17 @@ compile + + org.postgresql + postgresql + runtime + + + + org.springframework.boot + spring-boot-starter-data-jpa + + ru.practicum stat-client diff --git a/ewm-service/schema.png b/ewm-service/schema.png new file mode 100644 index 0000000..fdde26e Binary files /dev/null and b/ewm-service/schema.png differ diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/client/StatsClient.java b/ewm-service/src/main/java/ru/practicum/evmsevice/client/StatsClient.java index 811b2a1..589c74c 100644 --- a/ewm-service/src/main/java/ru/practicum/evmsevice/client/StatsClient.java +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/client/StatsClient.java @@ -10,13 +10,17 @@ import org.springframework.web.util.DefaultUriBuilderFactory; import ru.practicum.statclient.BaseClient; import ru.practicum.statdto.HitDto; +import ru.practicum.statdto.StatsDto; +import java.time.LocalDateTime; +import java.util.List; import java.util.Map; @Component public class StatsClient extends BaseClient { private static final String PREFIX_HIT = "/hit"; private static final String PREFIX_STATS = "/stats"; + private static final String PREFIX_EVENTS = "/events/"; @Autowired public StatsClient(@Value("${statserver.url}") String serverUrl, RestTemplateBuilder builder) { @@ -35,4 +39,33 @@ public void post(HitDto dto) { public ResponseEntity get(Map parameters) { return makeAndSendRequest(HttpMethod.GET, PREFIX_STATS, parameters, null); } + + public void hitInfo(String appName, String uri, String ip) { + HitDto hitDto = new HitDto(); + hitDto.setApp(appName); + hitDto.setUri(uri); + hitDto.setIp(ip); + hitDto.setTimestamp(LocalDateTime.now()); + post(hitDto); + } + + public Integer getEventViews(Integer eventId, Boolean unique) { + Map parameters = Map.of("uris", PREFIX_EVENTS + eventId, + "unique", unique); + List dtos = getStatsList(PREFIX_STATS, parameters); + if (dtos.isEmpty()) { + return 0; + } + return dtos.getFirst().getHits(); + } + + public List getEventViewsByUris(List eventUris, Boolean unique) { + StringBuilder urisBuilder = new StringBuilder(eventUris.getFirst()); + for (int i = 1; i < eventUris.size(); i++) { + urisBuilder.append(",").append(eventUris.get(i)); + } + Map parameters = Map.of("uris", urisBuilder.toString(), + "unique", unique); + return getStatsList(PREFIX_STATS, parameters); + } } diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/controller/AdminCategoriesController.java b/ewm-service/src/main/java/ru/practicum/evmsevice/controller/AdminCategoriesController.java new file mode 100644 index 0000000..0dd1e9c --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/controller/AdminCategoriesController.java @@ -0,0 +1,40 @@ +package ru.practicum.evmsevice.controller; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import ru.practicum.evmsevice.dto.CategoryDto; +import ru.practicum.evmsevice.dto.NewCategoryDto; +import ru.practicum.evmsevice.service.CategoryService; + +@Slf4j +@RequiredArgsConstructor +@RestController +@RequestMapping("/admin/categories") +public class AdminCategoriesController { + private final CategoryService categoryService; + + @PostMapping() + @ResponseStatus(HttpStatus.CREATED) + public CategoryDto createCategory(@Validated @RequestBody NewCategoryDto categoryDto) { + log.info("Создаем категорию {}.", categoryDto.getName()); + return categoryService.createCategory(categoryDto); + } + + @PatchMapping("/{id}") + @ResponseStatus(HttpStatus.OK) + public CategoryDto updateCategory(@Validated @RequestBody NewCategoryDto categoryDto, + @PathVariable int id) { + log.info("Обновляем категорию id={}.", id); + return categoryService.updateCategory(id, categoryDto); + } + + @DeleteMapping("/{id}") + @ResponseStatus(HttpStatus.NO_CONTENT) + public void deleteCategory(@PathVariable int id) { + log.info("Администратор удаляет категорию id={}.", id); + categoryService.deleteCategory(id); + } +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/controller/AdminCompilationsController.java b/ewm-service/src/main/java/ru/practicum/evmsevice/controller/AdminCompilationsController.java new file mode 100644 index 0000000..7c238f8 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/controller/AdminCompilationsController.java @@ -0,0 +1,41 @@ +package ru.practicum.evmsevice.controller; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import ru.practicum.evmsevice.dto.CompilationDto; +import ru.practicum.evmsevice.dto.NewCompilationDto; +import ru.practicum.evmsevice.dto.PatchCompilationDto; +import ru.practicum.evmsevice.service.CompilationService; + +@Slf4j +@RequiredArgsConstructor +@RestController +@RequestMapping("/admin/compilations") +public class AdminCompilationsController { + private final CompilationService compilationService; + + @PostMapping() + @ResponseStatus(HttpStatus.CREATED) + public CompilationDto createCompilation(@Validated @RequestBody NewCompilationDto newCompilationDto) { + log.info("Администратор создает подборку событий '{}'.", newCompilationDto.getTitle()); + return compilationService.createCompilation(newCompilationDto); + } + + @PatchMapping("/{compId}") + @ResponseStatus(HttpStatus.OK) + public CompilationDto updateCompilation(@PathVariable Integer compId, + @Validated @RequestBody PatchCompilationDto compilationDto) { + log.info("Администратор обновляет подборку событий '{}'.", compilationDto.getTitle()); + return compilationService.patchCompilation(compId, compilationDto); + } + + @DeleteMapping("/{compId}") + @ResponseStatus(HttpStatus.NO_CONTENT) + public void deleteCompilation(@PathVariable Integer compId) { + log.info("Администратор удаляет подборку событий id={}.", compId); + compilationService.deleteCompilation(compId); + } +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/controller/AdminEventsController.java b/ewm-service/src/main/java/ru/practicum/evmsevice/controller/AdminEventsController.java new file mode 100644 index 0000000..2c348d6 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/controller/AdminEventsController.java @@ -0,0 +1,46 @@ +package ru.practicum.evmsevice.controller; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import ru.practicum.evmsevice.dto.EventFullDto; +import ru.practicum.evmsevice.dto.UpdateEventAdminRequest; +import ru.practicum.evmsevice.service.EventService; + +import java.util.List; + +/** + * Класс обработки запросов администратора + */ +@Slf4j +@RequiredArgsConstructor +@RestController +@RequestMapping("/admin/events") +public class AdminEventsController { + private final EventService eventService; + + @GetMapping() + @ResponseStatus(HttpStatus.OK) + public List findEvents( + @RequestParam(name = "users", required = false) List users, + @RequestParam(name = "states", required = false) List states, + @RequestParam(name = "categories", required = false) List categories, + @RequestParam(name = "rangeStart", required = false) String rangeStart, + @RequestParam(name = "rangeEnd", required = false) String rangeEnd, + @RequestParam(name = "from", defaultValue = "0") Integer from, + @RequestParam(name = "size", defaultValue = "10") Integer size) { + log.info("Администратор запрашивает список событий. users:{}, states:{}, categories:{} rangeStart:{}, rangeStart:{}.", + users, states, categories, rangeStart, rangeEnd); + return eventService.findEventsByAdmin(states, users, categories, rangeStart, rangeEnd, from, size); + } + + @PatchMapping("/{eventId}") + @ResponseStatus(HttpStatus.OK) + public EventFullDto updateEvent(@PathVariable Integer eventId, + @RequestBody @Validated UpdateEventAdminRequest eventDto) { + log.info("Администратор редактирует событие id={}. {}", eventId, eventDto); + return eventService.adminUpdateEvent(eventId, eventDto); + } +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/controller/AdminUsersController.java b/ewm-service/src/main/java/ru/practicum/evmsevice/controller/AdminUsersController.java new file mode 100644 index 0000000..b966249 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/controller/AdminUsersController.java @@ -0,0 +1,65 @@ +package ru.practicum.evmsevice.controller; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import ru.practicum.evmsevice.dto.UserDto; +import ru.practicum.evmsevice.mapper.UserMapper; +import ru.practicum.evmsevice.model.User; +import ru.practicum.evmsevice.service.UserService; + +import java.util.List; + +@Slf4j +@RequiredArgsConstructor +@RestController +@RequestMapping("/admin/users") +public class AdminUsersController { + private final UserService userService; + + @GetMapping() + @ResponseStatus(HttpStatus.OK) + public List getUsers( + @RequestParam(name = "ids", required = false) List ids, + @RequestParam(name = "from", defaultValue = "0") Integer from, + @RequestParam(name = "size", defaultValue = "10") Integer size) { + log.info("Администратор запрашивает запрашивает список пользователей. {}", ids); + List users; + if (ids != null) { + users = userService.getUsers(ids); + } else { + users = userService.getUsers(); + } + return users.stream() + .map(UserMapper::toUserDto) + .skip(from) + .limit(size) + .toList(); + } + + @GetMapping("/{id}") + @ResponseStatus(HttpStatus.OK) + public UserDto getUser(@PathVariable Integer id) { + log.info("Выполняем поиск пользователя id={}.", id); + User user = userService.getUserById(id); + return UserMapper.toUserDto(user); + } + + @PostMapping() + @ResponseStatus(HttpStatus.CREATED) + public UserDto createUser(@Validated @RequestBody UserDto userDto) { + log.info("Создаем нового пользователя {}", userDto.toString()); + User savedUser = userService.addUser(UserMapper.toUser(userDto)); + return UserMapper.toUserDto(savedUser); + } + + @DeleteMapping("/{id}") + @ResponseStatus(HttpStatus.NO_CONTENT) + public void deleteUser(@PathVariable Integer id) { + log.info("Удаляем пользователя {}", id); + userService.deleteUser(id); + } + +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/controller/ErrorAdvisor.java b/ewm-service/src/main/java/ru/practicum/evmsevice/controller/ErrorAdvisor.java new file mode 100644 index 0000000..872f0f3 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/controller/ErrorAdvisor.java @@ -0,0 +1,159 @@ +package ru.practicum.evmsevice.controller; + +import jakarta.validation.ConstraintViolationException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; +import ru.practicum.evmsevice.dto.ApiError; +import ru.practicum.evmsevice.exception.*; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * Класс обработки исключений при обработке поступивших http запросов + */ +@Slf4j +@RestControllerAdvice +public class ErrorAdvisor { + @ExceptionHandler(BadRequestException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ApiError onHandlerMethodValidationException(BadRequestException e) { + log.error("400 {}.", e.getMessage()); + ApiError apiError = new ApiError(); + apiError.setStatus(HttpStatus.BAD_REQUEST); + apiError.setReason("Запрос составлен некорректно."); + apiError.setMessage(e.getMessage()); + apiError.setTimestamp(LocalDateTime.now()); + return apiError; + } + + @ExceptionHandler(NotFoundException.class) + @ResponseStatus(HttpStatus.NOT_FOUND) + public ApiError notFoundObject(NotFoundException exception) { + log.error("404 {}.", exception.getMessage()); + ApiError apiError = new ApiError(); + apiError.setStatus(HttpStatus.NOT_FOUND); + apiError.setReason("Запрошенный объект не найден."); + apiError.setMessage(exception.getMessage()); + apiError.setTimestamp(LocalDateTime.now()); + return apiError; + } + + @ExceptionHandler(ValidationException.class) + @ResponseStatus(HttpStatus.FORBIDDEN) + public ApiError onValidationException(ValidationException exception) { + log.error("409 {}.", exception.getMessage()); + ApiError apiError = new ApiError(); + apiError.setStatus(HttpStatus.FORBIDDEN); + apiError.setReason("Запрос содержит недопустимые данные."); + apiError.setMessage(exception.getMessage()); + apiError.setTimestamp(LocalDateTime.now()); + return apiError; + } + + @ExceptionHandler(InternalServerException.class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public ApiError onInternalException(final InternalServerException e) { + log.error("500 {}", e.getMessage()); + ApiError apiError = new ApiError(); + apiError.setStatus(HttpStatus.INTERNAL_SERVER_ERROR); + apiError.setReason("Внутренняя ошибка сервера."); + apiError.setMessage(e.getMessage()); + apiError.setTimestamp(LocalDateTime.now()); + return apiError; + } + + @ExceptionHandler(DataConflictException.class) + @ResponseStatus(HttpStatus.CONFLICT) + public ApiError onDataIntegrityViolationException(final DataConflictException e) { + log.error("409 {}", e.getMessage()); + ApiError apiError = new ApiError(); + apiError.setStatus(HttpStatus.CONFLICT); + apiError.setReason("Конфликт данных."); + apiError.setMessage(e.getMessage()); + apiError.setTimestamp(LocalDateTime.now()); + return apiError; + } + + @ExceptionHandler(DataIntegrityViolationException.class) + @ResponseStatus(HttpStatus.CONFLICT) + public ApiError onDataIntegrityViolationException(final DataIntegrityViolationException e) { + log.error("409 {}", e.getMessage()); + ApiError apiError = new ApiError(); + apiError.setStatus(HttpStatus.CONFLICT); + apiError.setReason(e.getRootCause().getMessage()); + apiError.setMessage(e.getMessage()); + apiError.setTimestamp(LocalDateTime.now()); + return apiError; + } + + @ExceptionHandler(MethodArgumentNotValidException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ApiError onMethodArgumentNotValidException(MethodArgumentNotValidException e) { + log.error("400 {}.", e.getMessage()); + final List violations = e.getBindingResult().getFieldErrors().stream() + .map(error -> String.format("Field: %s. Error: %s. Value: '%s'. ", + error.getField(), + error.getDefaultMessage(), + error.getRejectedValue() + )) + .toList(); + ApiError apiError = new ApiError(); + apiError.setStatus(HttpStatus.BAD_REQUEST); + apiError.setReason("Запрос сформирован некорректно."); + apiError.setMessage(String.join(" ", violations)); + apiError.setTimestamp(LocalDateTime.now()); + return apiError; + } + + @ExceptionHandler({ConstraintViolationException.class}) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ApiError onConstraintValidationException(ConstraintViolationException e) { + log.error("400 {}.", e.getMessage()); + final List violations = e.getConstraintViolations().stream() + .map( + violation -> String.format("Field: %s. Error: %s. Value: '%s'. ", + violation.getPropertyPath().toString(), + violation.getMessage(), + violation.getInvalidValue() + )) + .toList(); + ApiError apiError = new ApiError(); + apiError.setStatus(HttpStatus.BAD_REQUEST); + apiError.setReason("Запрос сформирован некорректно."); + apiError.setMessage(String.join(" ", violations)); + apiError.setTimestamp(LocalDateTime.now()); + return apiError; + } + + @ExceptionHandler(MethodArgumentTypeMismatchException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ApiError onMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e) { + log.error("400 {}.", e.getMessage()); + ApiError apiError = new ApiError(); + apiError.setStatus(HttpStatus.BAD_REQUEST); + apiError.setReason("Incorrectly made request."); + apiError.setMessage(e.getMessage()); + apiError.setTimestamp(LocalDateTime.now()); + return apiError; + } + + @ExceptionHandler + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public ApiError handleException(final Exception e) { + log.error("500 INTERNAL_SERVER_ERROR", e); + ApiError apiError = new ApiError(); + apiError.setStatus(HttpStatus.INTERNAL_SERVER_ERROR); + apiError.setReason(e.getCause().getMessage()); + apiError.setMessage(e.getMessage()); + apiError.setTimestamp(LocalDateTime.now()); + return apiError; + } + +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/controller/PublicController.java b/ewm-service/src/main/java/ru/practicum/evmsevice/controller/PublicController.java new file mode 100644 index 0000000..f2d57af --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/controller/PublicController.java @@ -0,0 +1,111 @@ +package ru.practicum.evmsevice.controller; + +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; +import ru.practicum.evmsevice.client.StatsClient; +import ru.practicum.evmsevice.dto.CategoryDto; +import ru.practicum.evmsevice.dto.CompilationDto; +import ru.practicum.evmsevice.dto.EventFullDto; +import ru.practicum.evmsevice.dto.EventShortDto; +import ru.practicum.evmsevice.enums.EventState; +import ru.practicum.evmsevice.exception.NotFoundException; +import ru.practicum.evmsevice.mapper.CategoryMapper; +import ru.practicum.evmsevice.mapper.EventMapper; +import ru.practicum.evmsevice.model.Event; +import ru.practicum.evmsevice.service.CategoryService; +import ru.practicum.evmsevice.service.CompilationService; +import ru.practicum.evmsevice.service.EventService; + +import java.util.List; + +@Slf4j +@RequiredArgsConstructor +@RestController +@RequestMapping() +public class PublicController { + private final StatsClient statsClient; + private final EventService eventService; + private final CompilationService compilationService; + private final CategoryService categoryService; + @Value("${spring.application.name}") + private String appName; + + @GetMapping("/events/{id}") + @ResponseStatus(HttpStatus.OK) + public EventFullDto findEventById(@PathVariable("id") int id, + HttpServletRequest request) { + log.info("Пользователь запрашивает для просмотра событие: {}", id); + Event event = eventService.findEventById(id); + // Событие должно быть опубликовано + if (!event.getState().equals(EventState.PUBLISHED)) { + throw new NotFoundException("Среди опубликованных не найдено событие id=" + id); + } + // сохраняем запрос в сервере статистики + statsClient.hitInfo(appName, request.getRequestURI(), request.getRemoteAddr()); + return EventMapper.toFullDto(event); + } + + @GetMapping("/events") + @ResponseStatus(HttpStatus.OK) + public List findAllEvents( + @RequestParam(name = "text", required = false) String text, + @RequestParam(name = "categories", required = false) List categories, + @RequestParam(name = "paid", required = false) Boolean paid, + @RequestParam(name = "rangeStart", required = false) + String rangeStart, + @RequestParam(name = "rangeEnd", required = false) + String rangeEnd, + @RequestParam(name = "onlyAvailable", defaultValue = "false") Boolean onlyAvailable, + @RequestParam(name = "sort", defaultValue = "EVENT_DATE") String sort, + @RequestParam(name = "from", defaultValue = "0") Integer from, + @RequestParam(name = "size", defaultValue = "10") Integer size, + HttpServletRequest request) { + log.info("Пользователь запрашивает поиск событий: содержащих текст:{}, categories:{}, rangeStart:{}, rangeEnd:{}.", + text, categories, rangeStart, rangeEnd); + List eventDtos = eventService.findEventsByParametrs(text, categories, + paid, rangeStart, rangeEnd, onlyAvailable, sort, from, size); + for (EventShortDto eventDto : eventDtos) { + statsClient.hitInfo(appName, String.format("/events/%d", eventDto.getId()), request.getRemoteAddr()); + } + return eventDtos; + } + + @GetMapping("/compilations") + @ResponseStatus(HttpStatus.OK) + public List findAllCompilations( + @RequestParam(name = "pinned", required = false) Boolean pinned, + @RequestParam(name = "from", defaultValue = "0") Integer from, + @RequestParam(name = "size", defaultValue = "10") Integer size) { + log.info("Пользователь запрашивает список подборок."); + return compilationService.getCompilations(pinned, from, size); + } + + @GetMapping("/compilations/{compId}") + @ResponseStatus(HttpStatus.OK) + public CompilationDto findCompilationById(@PathVariable("compId") int compId) { + log.info("Пользователь запрашивает подборку id={}.", compId); + return compilationService.getCompilation(compId); + } + + @GetMapping("/categories") + @ResponseStatus(HttpStatus.OK) + public List findCategories(@RequestParam(name = "from", defaultValue = "0") Integer from, + @RequestParam(name = "size", defaultValue = "10") Integer size) { + log.info("Пользователь запрашивает список категорий."); + return categoryService.getAllCategories().stream() + .map(CategoryMapper::toDto) + .skip(from).limit(size) + .toList(); + } + + @GetMapping("/categories/{catId}") + @ResponseStatus(HttpStatus.OK) + public CategoryDto findCategoryById(@PathVariable("catId") int catId) { + log.info("Пользователь запрашивает категорию id={}.", catId); + return CategoryMapper.toDto(categoryService.getCategoryById(catId)); + } +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/controller/TestClientController.java b/ewm-service/src/main/java/ru/practicum/evmsevice/controller/TestClientController.java index c817743..e51b2a2 100644 --- a/ewm-service/src/main/java/ru/practicum/evmsevice/controller/TestClientController.java +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/controller/TestClientController.java @@ -30,7 +30,7 @@ public void hit(@RequestBody HitDto dto) { @GetMapping("/stats") @ResponseStatus(HttpStatus.OK) - public ResponseEntity getStats( + public String getStats( @RequestParam(required = false) String start, @RequestParam(required = false) String end, @RequestParam(required = false) String uris, @@ -45,7 +45,7 @@ public ResponseEntity getStats( if (unique != null) parameters.put("unique", unique); if (size != null) parameters.put("size", size); ResponseEntity response = statsClient.get(parameters); - return response; + return response.getBody().toString(); } } diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/controller/UserController.java b/ewm-service/src/main/java/ru/practicum/evmsevice/controller/UserController.java new file mode 100644 index 0000000..29f8748 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/controller/UserController.java @@ -0,0 +1,106 @@ +package ru.practicum.evmsevice.controller; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import ru.practicum.evmsevice.dto.*; +import ru.practicum.evmsevice.mapper.RequestMapper; +import ru.practicum.evmsevice.model.Request; +import ru.practicum.evmsevice.service.EventService; +import ru.practicum.evmsevice.service.RequestService; + +import java.util.List; + +@Slf4j +@RequiredArgsConstructor +@RestController +@RequestMapping("/users") +public class UserController { + private final EventService eventService; + private final RequestService requestService; + + @PostMapping("/{id}/events") + @ResponseStatus(HttpStatus.CREATED) + public EventFullDto createEvent(@PathVariable int id, + @Validated @RequestBody NewEventDto eventDto) { + log.info("Пользователь id={} cоздает новое событие: {}", id, eventDto.toString()); + return eventService.createEvent(eventDto, id); + } + + @GetMapping("/{userId}/events/{eventId}") + @ResponseStatus(HttpStatus.OK) + public EventFullDto getEvent(@PathVariable int userId, + @PathVariable int eventId) { + + log.info("Пользователь id={} запрашивает информацию о событии id={}. ", + userId, eventId); + return eventService.getEventById(eventId, userId); + } + + @GetMapping("/{userId}/events") + @ResponseStatus(HttpStatus.OK) + public List getEvents(@PathVariable int userId, + @RequestParam(name = "from", defaultValue = "0") Integer from, + @RequestParam(name = "size", defaultValue = "10") Integer size) { + log.info("Пользователь id={} запрашивает информацию об инциированных событиях.", userId); + return eventService.getEventsByUserId(userId, from, size); + } + + @PatchMapping("/{userId}/events/{eventId}") + @ResponseStatus(HttpStatus.OK) + public EventFullDto updateEvent(@PathVariable Integer userId, + @PathVariable Integer eventId, + @Validated @RequestBody UpdateEventUserRequest eventDto) { + log.info("Пользователь id={} изменяет информацию об инициированном событии. {}", userId, eventDto.toString()); + return eventService.patchEvent(eventId, eventDto, userId); + } + + @GetMapping("/{userId}/events/{eventId}/requests") + @ResponseStatus(HttpStatus.OK) + public List findRequestsByEventId(@PathVariable int userId, + @PathVariable int eventId) { + log.info("Пользователь id={} выполняет поиск запросов на участие в событии id={}.", + userId, eventId); + return requestService.getRequestsByEventId(userId, eventId) + .stream() + .map(RequestMapper::toRequestDto) + .toList(); + } + + @PatchMapping("/{userId}/events/{eventId}/requests") + @ResponseStatus(HttpStatus.OK) + public RequestGroupDto patchRequestsByEventId(@PathVariable int userId, + @PathVariable int eventId, + @RequestBody RequestUpdateDto requestUpdateDto) { + log.info("Пользователь id={} модерирует запросы на событие id={}.", + userId, eventId); + return requestService.updateRequestsStatus(userId, eventId, requestUpdateDto); + } + + @PostMapping("/{userId}/requests") + @ResponseStatus(HttpStatus.CREATED) + public RequestDto createRequest(@PathVariable Integer userId, + @RequestParam(name = "eventId") Integer eventId) { + log.info("Пользователь id={} создает запрос на участие в событии id={}.", + userId, eventId); + Request request = requestService.createRequest(userId, eventId); + return RequestMapper.toRequestDto(request); + } + + @GetMapping("/{userId}/requests") + @ResponseStatus(HttpStatus.OK) + public List findRequestsByUserId(@PathVariable Integer userId) { + log.info("Пользователь id={} выполняет поиск собственных заявок.", userId); + return requestService.getRequestsByUserId(userId); + } + + @PatchMapping("/{userId}/requests/{requestId}/cancel") + @ResponseStatus(HttpStatus.OK) + public RequestDto canceledRequestById(@PathVariable Integer userId, + @PathVariable Integer requestId) { + log.info("Пользователь id={} отменяет запрос id={}.", userId, requestId); + return RequestMapper.toRequestDto(requestService.canceledRequest(userId, requestId)); + } +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/dto/ApiError.java b/ewm-service/src/main/java/ru/practicum/evmsevice/dto/ApiError.java new file mode 100644 index 0000000..6da48c6 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/dto/ApiError.java @@ -0,0 +1,25 @@ +package ru.practicum.evmsevice.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.springframework.http.HttpStatus; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +@Setter +@Getter +@NoArgsConstructor +public class ApiError { + private HttpStatus status; + private String reason; + private String message; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime timestamp; + @JsonIgnore + private List errors = new ArrayList<>(); +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/dto/CategoryDto.java b/ewm-service/src/main/java/ru/practicum/evmsevice/dto/CategoryDto.java new file mode 100644 index 0000000..4f8cbdd --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/dto/CategoryDto.java @@ -0,0 +1,17 @@ +package ru.practicum.evmsevice.dto; + +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Setter +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class CategoryDto { + private Integer id; + @Size(min = 1, max = 50, message = "Максимальная длина описания - 50 символов.") + private String name; +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/dto/CompilationDto.java b/ewm-service/src/main/java/ru/practicum/evmsevice/dto/CompilationDto.java new file mode 100644 index 0000000..d292b6a --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/dto/CompilationDto.java @@ -0,0 +1,20 @@ +package ru.practicum.evmsevice.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.List; + +@Setter +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class CompilationDto { + private int id; + private List events = new ArrayList<>(); + private String title; + private Boolean pinned; +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/dto/EventFullDto.java b/ewm-service/src/main/java/ru/practicum/evmsevice/dto/EventFullDto.java new file mode 100644 index 0000000..b83d48f --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/dto/EventFullDto.java @@ -0,0 +1,37 @@ +package ru.practicum.evmsevice.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import jakarta.validation.constraints.Size; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import ru.practicum.evmsevice.enums.EventState; +import ru.practicum.evmsevice.model.Location; + +import java.time.LocalDateTime; + +@Setter +@Getter +@NoArgsConstructor +public class EventFullDto { + private String annotation; + private CategoryDto category; + private Integer confirmedRequests; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime createdOn; + private String description; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime eventDate; + private Integer id; + private UserShortDto initiator; + private Location location; + private Boolean paid; + private Integer participantLimit; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime publishedOn; + private Boolean requestModeration; + private EventState state; + @Size(min = 3, max = 120, message = "длина заголовка 3 - 120 символов.") + private String title; + private Integer views; +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/dto/EventShortDto.java b/ewm-service/src/main/java/ru/practicum/evmsevice/dto/EventShortDto.java new file mode 100644 index 0000000..f8aa42c --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/dto/EventShortDto.java @@ -0,0 +1,30 @@ +package ru.practicum.evmsevice.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +import java.time.LocalDateTime; + +@Setter +@Getter +@ToString +@NoArgsConstructor +public class EventShortDto { + private Integer id; + private String annotation; + private CategoryDto category; + private Integer confirmedRequests; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime eventDate; + private UserShortDto initiator; + private Boolean paid; + private String title; + private Integer views; + @JsonIgnore + private Integer participantLimit; + +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/dto/NewCategoryDto.java b/ewm-service/src/main/java/ru/practicum/evmsevice/dto/NewCategoryDto.java new file mode 100644 index 0000000..5fc438d --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/dto/NewCategoryDto.java @@ -0,0 +1,16 @@ +package ru.practicum.evmsevice.dto; + +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Setter +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class NewCategoryDto { + @Size(min = 1, max = 50, message = "Максимальная длина описания - 50 символов.") + private String name; +} \ No newline at end of file diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/dto/NewCompilationDto.java b/ewm-service/src/main/java/ru/practicum/evmsevice/dto/NewCompilationDto.java new file mode 100644 index 0000000..21e37ef --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/dto/NewCompilationDto.java @@ -0,0 +1,25 @@ +package ru.practicum.evmsevice.dto; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.List; + +@Setter +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class NewCompilationDto { + private List events = new ArrayList<>(); + @NotEmpty(message = "Заголовок подборки не может быть пустым.") + @NotBlank(message = "Заголовок подборки не может быть пустым.") + @Size(min = 2, max = 50, message = "длина описания 2 - 50 символов.") + private String title; + private Boolean pinned; +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/dto/NewEventDto.java b/ewm-service/src/main/java/ru/practicum/evmsevice/dto/NewEventDto.java new file mode 100644 index 0000000..c350c20 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/dto/NewEventDto.java @@ -0,0 +1,41 @@ +package ru.practicum.evmsevice.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import jakarta.validation.constraints.*; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import ru.practicum.evmsevice.model.Location; + +import java.time.LocalDateTime; + +@Setter +@Getter +@NoArgsConstructor +@ToString +public class NewEventDto { + @NotEmpty(message = "Аннотация не может быть пустой.") + @NotBlank(message = "Аннотация не может быть пустой.") + @Size(min = 20, max = 2000, message = "длина аннотации 20 - 2000 символов.") + private String annotation; + @NotNull(message = "Категоря должна быть определена") + private Integer category; + @NotEmpty(message = "Описание события не может быть пустым.") + @NotBlank(message = "Описание события не может быть пустым.") + @Size(min = 20, max = 7000, message = "длина описания 20 - 7000 символов.") + private String description; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Future(message = "дата события не должна быть уже наступившей.") + private LocalDateTime eventDate; + @NotNull(message = "Место события должно быть определено.") + private Location location; + private Boolean paid; + @PositiveOrZero + private Integer participantLimit; + private Boolean requestModeration; + @NotEmpty(message = "Заголовок не может быть пустым.") + @NotBlank(message = "Заголовок не может быть пустым.") + @Size(min = 3, max = 120, message = "длина заголовка 3 - 120 символов.") + private String title; +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/dto/PatchCompilationDto.java b/ewm-service/src/main/java/ru/practicum/evmsevice/dto/PatchCompilationDto.java new file mode 100644 index 0000000..8ffe0af --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/dto/PatchCompilationDto.java @@ -0,0 +1,22 @@ +package ru.practicum.evmsevice.dto; + +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.List; + +@Setter +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class PatchCompilationDto { + private List events = new ArrayList<>(); + @Size(min = 2, max = 50, message = "длина описания 2 - 50 символов.") + private String title; + private Boolean pinned; + +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/dto/RequestDto.java b/ewm-service/src/main/java/ru/practicum/evmsevice/dto/RequestDto.java new file mode 100644 index 0000000..606f4f4 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/dto/RequestDto.java @@ -0,0 +1,21 @@ +package ru.practicum.evmsevice.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.*; +import ru.practicum.evmsevice.enums.RequestStatus; + +import java.time.LocalDateTime; + +@Setter +@Getter +@NoArgsConstructor +@AllArgsConstructor +@ToString +public class RequestDto { + private Integer id; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime created; + private Integer event; + private Integer requester; + private RequestStatus status; +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/dto/RequestGroupDto.java b/ewm-service/src/main/java/ru/practicum/evmsevice/dto/RequestGroupDto.java new file mode 100644 index 0000000..5ea6364 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/dto/RequestGroupDto.java @@ -0,0 +1,18 @@ +package ru.practicum.evmsevice.dto; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +import java.util.ArrayList; +import java.util.List; + +@Setter +@Getter +@NoArgsConstructor +@ToString +public class RequestGroupDto { + List confirmedRequests = new ArrayList<>(); + List rejectedRequests = new ArrayList<>(); +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/dto/RequestUpdateDto.java b/ewm-service/src/main/java/ru/practicum/evmsevice/dto/RequestUpdateDto.java new file mode 100644 index 0000000..da36867 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/dto/RequestUpdateDto.java @@ -0,0 +1,19 @@ +package ru.practicum.evmsevice.dto; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import ru.practicum.evmsevice.enums.RequestStatus; + +import java.util.ArrayList; +import java.util.List; + +@Setter +@Getter +@NoArgsConstructor +@ToString +public class RequestUpdateDto { + List requestIds = new ArrayList<>(); + RequestStatus status; +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/dto/UpdateEventAdminRequest.java b/ewm-service/src/main/java/ru/practicum/evmsevice/dto/UpdateEventAdminRequest.java new file mode 100644 index 0000000..c668309 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/dto/UpdateEventAdminRequest.java @@ -0,0 +1,37 @@ +package ru.practicum.evmsevice.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import jakarta.validation.constraints.Future; +import jakarta.validation.constraints.Positive; +import jakarta.validation.constraints.Size; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import ru.practicum.evmsevice.enums.EventAdminAction; +import ru.practicum.evmsevice.model.Location; + +import java.time.LocalDateTime; + +@Setter +@Getter +@NoArgsConstructor +@ToString +public class UpdateEventAdminRequest { + @Size(min = 20, max = 2000, message = "длина аннотации 20 - 2000 символов.") + private String annotation; + private Integer category; + @Size(min = 20, max = 7000, message = "длина описания 20 - 7000 символов.") + private String description; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Future(message = "дата события не должна быть уже наступившей.") + private LocalDateTime eventDate; + private Location location; + private Boolean paid; + @Positive(message = "число участников должно быть положительным") + private Integer participantLimit; + private Boolean requestModeration; + @Size(min = 3, max = 120, message = "длина заголовка 3 - 120 символов.") + private String title; + private EventAdminAction stateAction; +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/dto/UpdateEventUserRequest.java b/ewm-service/src/main/java/ru/practicum/evmsevice/dto/UpdateEventUserRequest.java new file mode 100644 index 0000000..d8d8627 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/dto/UpdateEventUserRequest.java @@ -0,0 +1,37 @@ +package ru.practicum.evmsevice.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import jakarta.validation.constraints.Future; +import jakarta.validation.constraints.Positive; +import jakarta.validation.constraints.Size; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import ru.practicum.evmsevice.enums.EventUserAction; +import ru.practicum.evmsevice.model.Location; + +import java.time.LocalDateTime; + +@Setter +@Getter +@NoArgsConstructor +@ToString +public class UpdateEventUserRequest { + @Size(min = 20, max = 2000, message = "длина аннотации 20 - 2000 символов.") + private String annotation; + private Integer category; + @Size(min = 20, max = 7000, message = "длина описания 20 - 7000 символов.") + private String description; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Future(message = "дата события не должна быть уже наступившей.") + private LocalDateTime eventDate; + private Location location; + private Boolean paid; + @Positive(message = "число участников должно быть положительным") + private Integer participantLimit; + private Boolean requestModeration; + @Size(min = 3, max = 120, message = "длина заголовка 3 - 120 символов.") + private String title; + private EventUserAction stateAction; +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/dto/UserDto.java b/ewm-service/src/main/java/ru/practicum/evmsevice/dto/UserDto.java new file mode 100644 index 0000000..a47a36b --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/dto/UserDto.java @@ -0,0 +1,22 @@ +package ru.practicum.evmsevice.dto; + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import lombok.*; + +@Setter +@Getter +@NoArgsConstructor +@AllArgsConstructor +@ToString +public class UserDto { + private Integer id; + @NotBlank(message = "Имя не может быть пустым") + @Size(min = 2, max = 250, message = "длина имени должна быть 2 - 250 символов.") + private String name; + @NotBlank(message = "Email не может быть пустым") + @Email(message = "Email должен удовлетворять правилам формирования почтовых адресов.") + @Size(min = 6, max = 254, message = "длина имени должна быть 6 - 254 символов.") + private String email; +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/dto/UserShortDto.java b/ewm-service/src/main/java/ru/practicum/evmsevice/dto/UserShortDto.java new file mode 100644 index 0000000..f610a95 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/dto/UserShortDto.java @@ -0,0 +1,15 @@ +package ru.practicum.evmsevice.dto; + +import jakarta.validation.constraints.NotBlank; +import lombok.*; + +@Setter +@Getter +@NoArgsConstructor +@AllArgsConstructor +@ToString +public class UserShortDto { + private Integer id; + @NotBlank(message = "Имя не может быть пустым") + private String name; +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/enums/EventAdminAction.java b/ewm-service/src/main/java/ru/practicum/evmsevice/enums/EventAdminAction.java new file mode 100644 index 0000000..da25f78 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/enums/EventAdminAction.java @@ -0,0 +1,6 @@ +package ru.practicum.evmsevice.enums; + +public enum EventAdminAction { + PUBLISH_EVENT, + REJECT_EVENT; +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/enums/EventState.java b/ewm-service/src/main/java/ru/practicum/evmsevice/enums/EventState.java new file mode 100644 index 0000000..51db4ed --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/enums/EventState.java @@ -0,0 +1,19 @@ +package ru.practicum.evmsevice.enums; + +import java.util.Optional; + +public enum EventState { + PENDING, + PUBLISHED, + CANCELED, + REJECTED; + + public static Optional from(String state) { + for (EventState eventState : EventState.values()) { + if (eventState.name().equalsIgnoreCase(state)) { + return Optional.of(eventState); + } + } + return Optional.empty(); + } +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/enums/EventUserAction.java b/ewm-service/src/main/java/ru/practicum/evmsevice/enums/EventUserAction.java new file mode 100644 index 0000000..3bae47a --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/enums/EventUserAction.java @@ -0,0 +1,6 @@ +package ru.practicum.evmsevice.enums; + +public enum EventUserAction { + SEND_TO_REVIEW, + CANCEL_REVIEW; +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/enums/RequestStatus.java b/ewm-service/src/main/java/ru/practicum/evmsevice/enums/RequestStatus.java new file mode 100644 index 0000000..575137f --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/enums/RequestStatus.java @@ -0,0 +1,19 @@ +package ru.practicum.evmsevice.enums; + +import java.util.Optional; + +public enum RequestStatus { + PENDING, + CONFIRMED, + REJECTED, + CANCELED; + + public static Optional from(String state) { + for (RequestStatus status : RequestStatus.values()) { + if (status.name().equalsIgnoreCase(state)) { + return Optional.of(status); + } + } + return Optional.empty(); + } +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/exception/BadRequestException.java b/ewm-service/src/main/java/ru/practicum/evmsevice/exception/BadRequestException.java new file mode 100644 index 0000000..0072153 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/exception/BadRequestException.java @@ -0,0 +1,7 @@ +package ru.practicum.evmsevice.exception; + +public class BadRequestException extends RuntimeException { + public BadRequestException(String message) { + super(message); + } +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/exception/DataConflictException.java b/ewm-service/src/main/java/ru/practicum/evmsevice/exception/DataConflictException.java new file mode 100644 index 0000000..a72ed72 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/exception/DataConflictException.java @@ -0,0 +1,7 @@ +package ru.practicum.evmsevice.exception; + +public class DataConflictException extends RuntimeException { + public DataConflictException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/exception/InternalServerException.java b/ewm-service/src/main/java/ru/practicum/evmsevice/exception/InternalServerException.java new file mode 100644 index 0000000..8683da7 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/exception/InternalServerException.java @@ -0,0 +1,7 @@ +package ru.practicum.evmsevice.exception; + +public class InternalServerException extends RuntimeException { + public InternalServerException(String message) { + super(message); + } +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/exception/NotFoundException.java b/ewm-service/src/main/java/ru/practicum/evmsevice/exception/NotFoundException.java new file mode 100644 index 0000000..764150a --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/exception/NotFoundException.java @@ -0,0 +1,7 @@ +package ru.practicum.evmsevice.exception; + +public class NotFoundException extends RuntimeException { + public NotFoundException(String message) { + super(message); + } +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/exception/ValidationException.java b/ewm-service/src/main/java/ru/practicum/evmsevice/exception/ValidationException.java new file mode 100644 index 0000000..2a98665 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/exception/ValidationException.java @@ -0,0 +1,7 @@ +package ru.practicum.evmsevice.exception; + +public class ValidationException extends RuntimeException { + public ValidationException(String message) { + super(message); + } +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/mapper/CategoryMapper.java b/ewm-service/src/main/java/ru/practicum/evmsevice/mapper/CategoryMapper.java new file mode 100644 index 0000000..3143fe0 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/mapper/CategoryMapper.java @@ -0,0 +1,23 @@ +package ru.practicum.evmsevice.mapper; + +import ru.practicum.evmsevice.dto.CategoryDto; +import ru.practicum.evmsevice.dto.NewCategoryDto; +import ru.practicum.evmsevice.model.Category; + +public class CategoryMapper { + private CategoryMapper() { + } + + public static Category toCategory(NewCategoryDto dto) { + Category category = new Category(); + category.setName(dto.getName()); + return category; + } + + public static CategoryDto toDto(Category category) { + CategoryDto dto = new CategoryDto(); + dto.setId(category.getId()); + dto.setName(category.getName()); + return dto; + } +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/mapper/CompilationMapper.java b/ewm-service/src/main/java/ru/practicum/evmsevice/mapper/CompilationMapper.java new file mode 100644 index 0000000..27f1b3b --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/mapper/CompilationMapper.java @@ -0,0 +1,37 @@ +package ru.practicum.evmsevice.mapper; + +import ru.practicum.evmsevice.dto.CompilationDto; +import ru.practicum.evmsevice.dto.EventShortDto; +import ru.practicum.evmsevice.dto.NewCompilationDto; +import ru.practicum.evmsevice.model.Compilation; + +import java.util.List; + +public class CompilationMapper { + private CompilationMapper() { + } + + public static Compilation toCompilation(NewCompilationDto dto) { + Compilation c = new Compilation(); + c.setTitle(dto.getTitle()); + c.setPinned(false); + if (dto.getPinned() != null) { + c.setPinned(dto.getPinned()); + } + return c; + } + + public static CompilationDto toCompilationDto(Compilation c) { + CompilationDto dto = new CompilationDto(); + dto.setId(c.getId()); + dto.setTitle(c.getTitle()); + dto.setPinned(c.getPinned()); + List eventDtos = c.getEvents() + .stream() + .map(EventMapper::toShortDto) + .toList(); + dto.setEvents(eventDtos); + return dto; + } + +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/mapper/EventMapper.java b/ewm-service/src/main/java/ru/practicum/evmsevice/mapper/EventMapper.java new file mode 100644 index 0000000..6526d08 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/mapper/EventMapper.java @@ -0,0 +1,96 @@ +package ru.practicum.evmsevice.mapper; + +import ru.practicum.evmsevice.dto.EventFullDto; +import ru.practicum.evmsevice.dto.EventShortDto; +import ru.practicum.evmsevice.dto.NewEventDto; +import ru.practicum.evmsevice.enums.EventState; +import ru.practicum.evmsevice.model.Event; +import ru.practicum.evmsevice.model.Location; + +import java.time.LocalDateTime; + +public class EventMapper { + private EventMapper() { + } + + public static Event toEvent(final NewEventDto newDto) { + Event event = new Event(); + event.setAnnotation(newDto.getAnnotation()); + event.setDescription(newDto.getDescription()); + event.setEventDate(newDto.getEventDate()); + event.setCreatedOn(LocalDateTime.now()); + event.setLat(newDto.getLocation().getLat()); + event.setLon(newDto.getLocation().getLon()); + event.setPaid(false); + if (newDto.getPaid() != null) { + event.setPaid(newDto.getPaid()); + } + event.setParticipantLimit(0); + if (newDto.getParticipantLimit() != null) { + event.setParticipantLimit(newDto.getParticipantLimit()); + } + event.setRequestModeration(true); + if (newDto.getRequestModeration() != null) { + event.setRequestModeration(newDto.getRequestModeration()); + } + event.setState(EventState.PENDING); + event.setTitle(newDto.getTitle()); + event.setConfirmedRequests(0); + event.setViews(0); + return event; + } + + public static EventFullDto toFullDto(Event event) { + EventFullDto dto = new EventFullDto(); + dto.setId(event.getId()); + dto.setAnnotation(event.getAnnotation()); + dto.setDescription(event.getDescription()); + dto.setEventDate(event.getEventDate()); + dto.setCreatedOn(event.getCreatedOn()); + dto.setCategory(CategoryMapper.toDto(event.getCategory())); + dto.setInitiator(UserMapper.toUserShortDto(event.getInitiator())); + dto.setLocation(new Location(event.getLat(), event.getLon())); + dto.setParticipantLimit(0); + if (event.getParticipantLimit() != null) { + dto.setParticipantLimit(event.getParticipantLimit()); + } + dto.setRequestModeration(event.getRequestModeration()); + dto.setState(event.getState()); + dto.setPaid(event.getPaid()); + dto.setPublishedOn(event.getPublishedOn()); + dto.setTitle(event.getTitle()); + dto.setConfirmedRequests(0); + if (event.getConfirmedRequests() != null) { + dto.setConfirmedRequests(event.getConfirmedRequests()); + } + dto.setViews(0); + if (event.getViews() != null) { + dto.setViews(event.getViews()); + } + return dto; + } + + public static EventShortDto toShortDto(Event event) { + EventShortDto dto = new EventShortDto(); + dto.setId(event.getId()); + dto.setTitle(event.getTitle()); + dto.setAnnotation(event.getAnnotation()); + dto.setCategory(CategoryMapper.toDto(event.getCategory())); + dto.setInitiator(UserMapper.toUserShortDto(event.getInitiator())); + dto.setEventDate(event.getEventDate()); + dto.setPaid(event.getPaid()); + dto.setParticipantLimit(0); + if (event.getParticipantLimit() != null) { + dto.setParticipantLimit(event.getParticipantLimit()); + } + dto.setConfirmedRequests(0); + if (event.getConfirmedRequests() != null) { + dto.setConfirmedRequests(event.getConfirmedRequests()); + } + dto.setViews(0); + if (event.getViews() != null) { + dto.setViews(event.getViews()); + } + return dto; + } +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/mapper/RequestMapper.java b/ewm-service/src/main/java/ru/practicum/evmsevice/mapper/RequestMapper.java new file mode 100644 index 0000000..8323551 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/mapper/RequestMapper.java @@ -0,0 +1,19 @@ +package ru.practicum.evmsevice.mapper; + +import ru.practicum.evmsevice.dto.RequestDto; +import ru.practicum.evmsevice.model.Request; + +public class RequestMapper { + private RequestMapper() { + } + + public static RequestDto toRequestDto(Request request) { + RequestDto requestDto = new RequestDto(); + requestDto.setId(request.getId()); + requestDto.setCreated(request.getCreated()); + requestDto.setEvent(request.getEvent().getId()); + requestDto.setRequester(request.getRequester().getId()); + requestDto.setStatus(request.getStatus()); + return requestDto; + } +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/mapper/UserMapper.java b/ewm-service/src/main/java/ru/practicum/evmsevice/mapper/UserMapper.java new file mode 100644 index 0000000..a54b7b6 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/mapper/UserMapper.java @@ -0,0 +1,35 @@ +package ru.practicum.evmsevice.mapper; + +import ru.practicum.evmsevice.dto.UserDto; +import ru.practicum.evmsevice.dto.UserShortDto; +import ru.practicum.evmsevice.model.User; + +public class UserMapper { + private UserMapper() { + } + + public static User toUser(UserDto dto) { + User user = new User(); + if (dto.getId() != null) { + user.setId(dto.getId()); + } + user.setName(dto.getName()); + user.setEmail(dto.getEmail()); + return user; + } + + public static UserDto toUserDto(User user) { + UserDto dto = new UserDto(); + dto.setId(user.getId()); + dto.setName(user.getName()); + dto.setEmail(user.getEmail()); + return dto; + } + + public static UserShortDto toUserShortDto(User user) { + UserShortDto dto = new UserShortDto(); + dto.setId(user.getId()); + dto.setName(user.getName()); + return dto; + } +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/model/Category.java b/ewm-service/src/main/java/ru/practicum/evmsevice/model/Category.java new file mode 100644 index 0000000..65180a3 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/model/Category.java @@ -0,0 +1,23 @@ +package ru.practicum.evmsevice.model; + +import jakarta.persistence.*; +import jakarta.validation.constraints.NotBlank; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Entity +@Setter +@Getter +@Table(name = "categories", schema = "public") +@EqualsAndHashCode(of = {"name"}) +@NoArgsConstructor +public class Category { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + @Column(name = "name", nullable = false) + @NotBlank(message = "Название категории не может быть пустым") + private String name; +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/model/Compilation.java b/ewm-service/src/main/java/ru/practicum/evmsevice/model/Compilation.java new file mode 100644 index 0000000..1c64f63 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/model/Compilation.java @@ -0,0 +1,29 @@ +package ru.practicum.evmsevice.model; + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.HashSet; +import java.util.Set; + +@Entity +@Setter +@Getter +@Table(name = "compilations", schema = "public") +@NoArgsConstructor +public class Compilation { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + @ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.REFRESH) + @JoinTable(name = "eventlinks", + joinColumns = {@JoinColumn(name = "compilation_id")}, + inverseJoinColumns = {@JoinColumn(name = "event_id")}) + private Set events = new HashSet<>(); + @Column(name = "title", nullable = false) + private String title; + @Column(name = "pinned", nullable = false) + private Boolean pinned; +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/model/Event.java b/ewm-service/src/main/java/ru/practicum/evmsevice/model/Event.java new file mode 100644 index 0000000..7048508 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/model/Event.java @@ -0,0 +1,55 @@ +package ru.practicum.evmsevice.model; + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import ru.practicum.evmsevice.enums.EventState; + +import java.time.LocalDateTime; + +@Entity +@Setter +@Getter +@Table(name = "events", schema = "public") +@NoArgsConstructor +public class Event { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + @Column(name = "annotation", nullable = false) + private String annotation; + @ManyToOne + @JoinColumn(name = "category_id") + private Category category; + @Column(name = "createdon", nullable = false) + private LocalDateTime createdOn; + @Column(name = "description") + private String description; + @Column(name = "eventdate", nullable = false) + private LocalDateTime eventDate; + @ManyToOne + @JoinColumn(name = "initiator_id") + private User initiator; + @Column(name = "lat") + private Float lat; + @Column(name = "lon") + private Float lon; + @Column(name = "paid") + private Boolean paid; + @Column(name = "participantlimit") + private Integer participantLimit; + @Column(name = "publishedon") + private LocalDateTime publishedOn; + @Column(name = "requestmoderation") + private Boolean requestModeration; + @Column(name = "state") + @Enumerated(EnumType.STRING) + private EventState state; + @Column(name = "title") + private String title; + @Transient + private Integer confirmedRequests; + @Transient + private Integer views; +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/model/EventConfirmedRequestCount.java b/ewm-service/src/main/java/ru/practicum/evmsevice/model/EventConfirmedRequestCount.java new file mode 100644 index 0000000..7c4405d --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/model/EventConfirmedRequestCount.java @@ -0,0 +1,15 @@ +package ru.practicum.evmsevice.model; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Setter +@Getter +@AllArgsConstructor +@NoArgsConstructor +public class EventConfirmedRequestCount { + private Integer eventId; + private Long confirmedRequestCount; +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/model/Location.java b/ewm-service/src/main/java/ru/practicum/evmsevice/model/Location.java new file mode 100644 index 0000000..e5501ad --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/model/Location.java @@ -0,0 +1,15 @@ +package ru.practicum.evmsevice.model; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Setter +@Getter +@AllArgsConstructor +@NoArgsConstructor +public class Location { + private Float lat; + private Float lon; +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/model/Request.java b/ewm-service/src/main/java/ru/practicum/evmsevice/model/Request.java new file mode 100644 index 0000000..6e03ecb --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/model/Request.java @@ -0,0 +1,31 @@ +package ru.practicum.evmsevice.model; + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import ru.practicum.evmsevice.enums.RequestStatus; + +import java.time.LocalDateTime; + +@Entity +@Setter +@Getter +@Table(name = "requests", schema = "public") +@NoArgsConstructor +public class Request { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + @ManyToOne + @JoinColumn(name = "requester_id") + private User requester; + @ManyToOne + @JoinColumn(name = "event_id") + private Event event; + @Column(name = "status") + @Enumerated(EnumType.STRING) + private RequestStatus status; + @Column(name = "created", nullable = false) + private LocalDateTime created; +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/model/User.java b/ewm-service/src/main/java/ru/practicum/evmsevice/model/User.java new file mode 100644 index 0000000..390b7a7 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/model/User.java @@ -0,0 +1,26 @@ +package ru.practicum.evmsevice.model; + +import jakarta.persistence.*; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * Класс описания пользователя + */ +@Entity +@Setter +@Getter +@Table(name = "users", schema = "public") +@EqualsAndHashCode(of = {"name", "email"}) +@NoArgsConstructor +public class User { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + @Column(name = "name", nullable = false) + private String name; + @Column(name = "email", nullable = false) + private String email; +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/repository/CategoryRepository.java b/ewm-service/src/main/java/ru/practicum/evmsevice/repository/CategoryRepository.java new file mode 100644 index 0000000..0775f42 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/repository/CategoryRepository.java @@ -0,0 +1,7 @@ +package ru.practicum.evmsevice.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import ru.practicum.evmsevice.model.Category; + +public interface CategoryRepository extends JpaRepository { +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/repository/CompilationRepository.java b/ewm-service/src/main/java/ru/practicum/evmsevice/repository/CompilationRepository.java new file mode 100644 index 0000000..7a76ff3 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/repository/CompilationRepository.java @@ -0,0 +1,10 @@ +package ru.practicum.evmsevice.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import ru.practicum.evmsevice.model.Compilation; + +import java.util.List; + +public interface CompilationRepository extends JpaRepository { + List findAllByPinnedEquals(boolean pinned); +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/repository/EventRepository.java b/ewm-service/src/main/java/ru/practicum/evmsevice/repository/EventRepository.java new file mode 100644 index 0000000..5a3f866 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/repository/EventRepository.java @@ -0,0 +1,16 @@ +package ru.practicum.evmsevice.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import ru.practicum.evmsevice.model.Event; + +import java.util.List; + +public interface EventRepository extends JpaRepository, + JpaSpecificationExecutor { + List findEventsByInitiator_Id(int id); + + List findEventsByIdIn(List ids); + + List findEventsByCategory_Id(int id); +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/repository/EventSpecification.java b/ewm-service/src/main/java/ru/practicum/evmsevice/repository/EventSpecification.java new file mode 100644 index 0000000..daf8157 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/repository/EventSpecification.java @@ -0,0 +1,49 @@ +package ru.practicum.evmsevice.repository; + +import org.springframework.data.jpa.domain.Specification; +import ru.practicum.evmsevice.model.Event; + +import java.time.LocalDateTime; +import java.util.List; + +public class EventSpecification { + public static Specification annotetionContains(String text) { + return ((root, query, criteriaBuilder) -> + criteriaBuilder.like(root.get("annotation"), "%" + text + "%")); + } + + public static Specification descriptionContains(String text) { + return ((root, query, criteriaBuilder) -> + criteriaBuilder.like(root.get("description"), "%" + text + "%")); + } + + public static Specification categoryIn(List categories) { + return ((root, query, criteriaBuilder) -> + criteriaBuilder.in(root.join("category").get("id")).value(categories)); + } + + public static Specification paidEqual(Boolean paid) { + return ((root, query, criteriaBuilder) -> + criteriaBuilder.equal(root.get("paid"), paid)); + } + + public static Specification eventDateAfter(LocalDateTime startDate) { + return ((root, query, criteriaBuilder) -> + criteriaBuilder.greaterThanOrEqualTo(root.get("eventDate"), startDate)); + } + + public static Specification eventDateBefore(LocalDateTime endDate) { + return ((root, query, criteriaBuilder) -> + criteriaBuilder.lessThan(root.get("eventDate"), endDate)); + } + + public static Specification eventInitiatorIdIn(List initiatorIds) { + return ((root, query, criteriaBuilder) -> + criteriaBuilder.in(root.join("initiator").get("id")).value(initiatorIds)); + } + + public static Specification eventStateIn(List states) { + return ((root, query, criteriaBuilder) -> + criteriaBuilder.in(root.get("state")).value(states)); + } +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/repository/RequestRepository.java b/ewm-service/src/main/java/ru/practicum/evmsevice/repository/RequestRepository.java new file mode 100644 index 0000000..97cf0f7 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/repository/RequestRepository.java @@ -0,0 +1,23 @@ +package ru.practicum.evmsevice.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import ru.practicum.evmsevice.model.EventConfirmedRequestCount; +import ru.practicum.evmsevice.model.Request; + +import java.util.List; + +public interface RequestRepository extends JpaRepository { + List findAllByRequester_Id(int userId); + + List findAllByEvent_Id(int eventId); + + @Query("SELECT COUNT(r) FROM Request r WHERE r.event.id = :eventId AND r.status = 'CONFIRMED'") + Integer getCountConfirmedRequestsByEventId(@Param("eventId") int eventId); + + @Query("SELECT new ru.practicum.evmsevice.model.EventConfirmedRequestCount(r.event.id, COUNT(r)) " + + "FROM Request r WHERE r. event.id IN (:ids) AND r.status = 'CONFIRMED'" + + "GROUP BY r.event.id") + List getCountConfirmedRequests(@Param("ids") List ids); +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/repository/UserRepository.java b/ewm-service/src/main/java/ru/practicum/evmsevice/repository/UserRepository.java new file mode 100644 index 0000000..5887768 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/repository/UserRepository.java @@ -0,0 +1,7 @@ +package ru.practicum.evmsevice.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import ru.practicum.evmsevice.model.User; + +public interface UserRepository extends JpaRepository { +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/service/CategoryService.java b/ewm-service/src/main/java/ru/practicum/evmsevice/service/CategoryService.java new file mode 100644 index 0000000..c2cc218 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/service/CategoryService.java @@ -0,0 +1,19 @@ +package ru.practicum.evmsevice.service; + +import ru.practicum.evmsevice.dto.CategoryDto; +import ru.practicum.evmsevice.dto.NewCategoryDto; +import ru.practicum.evmsevice.model.Category; + +import java.util.List; + +public interface CategoryService { + CategoryDto createCategory(NewCategoryDto categoryDto); + + CategoryDto updateCategory(Integer id, NewCategoryDto categoryDto); + + void deleteCategory(Integer id); + + Category getCategoryById(Integer id); + + List getAllCategories(); +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/service/CategoryServiceImpl.java b/ewm-service/src/main/java/ru/practicum/evmsevice/service/CategoryServiceImpl.java new file mode 100644 index 0000000..2de8c9b --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/service/CategoryServiceImpl.java @@ -0,0 +1,67 @@ +package ru.practicum.evmsevice.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import ru.practicum.evmsevice.dto.CategoryDto; +import ru.practicum.evmsevice.dto.NewCategoryDto; +import ru.practicum.evmsevice.exception.DataConflictException; +import ru.practicum.evmsevice.exception.NotFoundException; +import ru.practicum.evmsevice.mapper.CategoryMapper; +import ru.practicum.evmsevice.model.Category; +import ru.practicum.evmsevice.model.Event; +import ru.practicum.evmsevice.repository.CategoryRepository; +import ru.practicum.evmsevice.repository.EventRepository; + +import java.util.List; + +@Service +@Transactional(readOnly = true) +@RequiredArgsConstructor +public class CategoryServiceImpl implements CategoryService { + private final CategoryRepository categoryRepository; + private final EventRepository eventRepository; + + @Override + @Transactional + public CategoryDto createCategory(NewCategoryDto categoryDto) { + Category savedCategory = categoryRepository.save(CategoryMapper.toCategory(categoryDto)); + return CategoryMapper.toDto(savedCategory); + } + + @Override + @Transactional + public CategoryDto updateCategory(Integer id, NewCategoryDto categoryDto) { + Category category = categoryRepository.findById(id) + .orElseThrow(() -> new NotFoundException("Не найдена категория id=" + id)); + category.setName(categoryDto.getName()); + Category updatedCategory = categoryRepository.save(category); + return CategoryMapper.toDto(updatedCategory); + } + + @Override + @Transactional + public void deleteCategory(Integer id) { + categoryRepository.findById(id) + .orElseThrow(() -> new NotFoundException("Не найдена категория id=" + id)); + List events = eventRepository.findEventsByCategory_Id(id); + if (events.size() > 0) { + throw new DataConflictException( + "Категория id=" + id + " не пустая."); + } + categoryRepository.deleteById(id); + } + + @Override + + public Category getCategoryById(Integer id) { + Category category = categoryRepository.findById(id) + .orElseThrow(() -> new NotFoundException("Не найдена категория id=" + id)); + return category; + } + + @Override + public List getAllCategories() { + return categoryRepository.findAll(); + } +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/service/CompilationService.java b/ewm-service/src/main/java/ru/practicum/evmsevice/service/CompilationService.java new file mode 100644 index 0000000..48b1d5c --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/service/CompilationService.java @@ -0,0 +1,19 @@ +package ru.practicum.evmsevice.service; + +import ru.practicum.evmsevice.dto.CompilationDto; +import ru.practicum.evmsevice.dto.NewCompilationDto; +import ru.practicum.evmsevice.dto.PatchCompilationDto; + +import java.util.List; + +public interface CompilationService { + CompilationDto createCompilation(NewCompilationDto compilationDto); + + CompilationDto patchCompilation(Integer compId, PatchCompilationDto compilationDto); + + void deleteCompilation(Integer compId); + + List getCompilations(Boolean pinned, Integer from, Integer size); + + CompilationDto getCompilation(Integer compId); +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/service/CompilationServiceImpl.java b/ewm-service/src/main/java/ru/practicum/evmsevice/service/CompilationServiceImpl.java new file mode 100644 index 0000000..b6ff4eb --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/service/CompilationServiceImpl.java @@ -0,0 +1,85 @@ +package ru.practicum.evmsevice.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import ru.practicum.evmsevice.dto.CompilationDto; +import ru.practicum.evmsevice.dto.NewCompilationDto; +import ru.practicum.evmsevice.dto.PatchCompilationDto; +import ru.practicum.evmsevice.exception.NotFoundException; +import ru.practicum.evmsevice.mapper.CompilationMapper; +import ru.practicum.evmsevice.model.Compilation; +import ru.practicum.evmsevice.model.Event; +import ru.practicum.evmsevice.repository.CompilationRepository; + +import java.util.HashSet; +import java.util.List; + +@Service +@Transactional +@RequiredArgsConstructor +public class CompilationServiceImpl implements CompilationService { + private final CompilationRepository compilationRepository; + private final EventService eventService; + + @Override + public CompilationDto createCompilation(NewCompilationDto compilationDto) { + Compilation compilation = CompilationMapper.toCompilation(compilationDto); + List events = eventService.findEventsByIdIn(compilationDto.getEvents()); + compilation.setEvents(new HashSet<>(events)); + Compilation savedCompilation = compilationRepository.save(compilation); + CompilationDto savedCompilationDto = CompilationMapper.toCompilationDto(savedCompilation); + return savedCompilationDto; + } + + @Override + public CompilationDto patchCompilation(Integer compId, PatchCompilationDto compilationDto) { + Compilation compilation = compilationRepository.findById(compId) + .orElseThrow(() -> new NotFoundException("Не найдена подборка id=" + compId)); + if (compilationDto.getTitle() != null) { + compilation.setTitle(compilationDto.getTitle()); + } + if (compilationDto.getPinned() != null) { + compilation.setPinned(compilationDto.getPinned()); + } + if (compilationDto.getEvents() != null) { + if (!compilationDto.getEvents().isEmpty()) { + List events = eventService.findEventsByIdIn(compilationDto.getEvents()); + compilation.setEvents(new HashSet<>(events)); + } + } + Compilation savedCompilation = compilationRepository.save(compilation); + CompilationDto savedCompilationDto = CompilationMapper.toCompilationDto(savedCompilation); + return savedCompilationDto; + } + + @Override + public void deleteCompilation(Integer compId) { + Compilation compilation = compilationRepository.findById(compId) + .orElseThrow(() -> new NotFoundException("Не найдена подборка id=" + compId)); + compilation.getEvents().clear(); + compilationRepository.delete(compilation); + } + + @Override + public CompilationDto getCompilation(Integer compId) { + Compilation compilation = compilationRepository.findById(compId) + .orElseThrow(() -> new NotFoundException("Не найдена подборка id=" + compId)); + return CompilationMapper.toCompilationDto(compilation); + } + + @Override + public List getCompilations(Boolean pinned, Integer from, Integer size) { + List compilations; + if (pinned != null) { + compilations = compilationRepository.findAllByPinnedEquals(pinned); + } else { + compilations = compilationRepository.findAll(); + } + return compilations.stream() + .map(CompilationMapper::toCompilationDto) + .skip(from) + .limit(size) + .toList(); + } +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/service/EventService.java b/ewm-service/src/main/java/ru/practicum/evmsevice/service/EventService.java new file mode 100644 index 0000000..531ee7a --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/service/EventService.java @@ -0,0 +1,39 @@ +package ru.practicum.evmsevice.service; + +import ru.practicum.evmsevice.dto.*; +import ru.practicum.evmsevice.model.Event; + +import java.util.List; + +public interface EventService { + EventFullDto createEvent(NewEventDto newEventDto, Integer userId); + + EventFullDto getEventById(Integer eventId, Integer userId); + + List getEventsByUserId(Integer userId, Integer from, Integer size); + + EventFullDto patchEvent(Integer eventId, UpdateEventUserRequest eventDto, Integer userId); + + EventFullDto adminUpdateEvent(Integer eventId, UpdateEventAdminRequest eventDto); + + Event findEventById(Integer eventId); + + List findEventsByParametrs(String text, + List categories, + Boolean paid, + String rangeStart, + String rangeEnd, + Boolean onlyAvailable, + String sort, + Integer from, Integer size); + + List findEventsByAdmin(List states, + List users, + List categories, + String rangeStart, + String rangeEnd, + Integer from, + Integer size); + + List findEventsByIdIn(List eventIds); +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/service/EventServiceImpl.java b/ewm-service/src/main/java/ru/practicum/evmsevice/service/EventServiceImpl.java new file mode 100644 index 0000000..5b0278d --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/service/EventServiceImpl.java @@ -0,0 +1,478 @@ +package ru.practicum.evmsevice.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; +import ru.practicum.evmsevice.client.StatsClient; +import ru.practicum.evmsevice.dto.*; +import ru.practicum.evmsevice.enums.EventAdminAction; +import ru.practicum.evmsevice.enums.EventState; +import ru.practicum.evmsevice.enums.EventUserAction; +import ru.practicum.evmsevice.exception.BadRequestException; +import ru.practicum.evmsevice.exception.DataConflictException; +import ru.practicum.evmsevice.exception.NotFoundException; +import ru.practicum.evmsevice.exception.ValidationException; +import ru.practicum.evmsevice.mapper.EventMapper; +import ru.practicum.evmsevice.model.Category; +import ru.practicum.evmsevice.model.Event; +import ru.practicum.evmsevice.model.EventConfirmedRequestCount; +import ru.practicum.evmsevice.model.User; +import ru.practicum.evmsevice.repository.EventRepository; +import ru.practicum.evmsevice.repository.EventSpecification; +import ru.practicum.evmsevice.repository.RequestRepository; +import ru.practicum.statdto.StatsDto; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.TreeMap; + +@Service +@Transactional +@RequiredArgsConstructor +public class EventServiceImpl implements EventService { + private static final DateTimeFormatter DATA_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + private static final Integer HOURS_EVENT_DELAY = 2; + + private final EventRepository eventRepository; + private final RequestRepository requestRepository; + private final UserService userService; + private final CategoryService categoryService; + private final StatsClient statsClient; + + /** + * Создание нового события + * + * @param newEventDto - новое событие + * @param userId - идентификатор пользователя инициатора + * @return - сохраненный объект информации о событии + */ + @Override + public EventFullDto createEvent(NewEventDto newEventDto, Integer userId) { + if (newEventDto.getEventDate().isBefore(LocalDateTime.now().plusHours(HOURS_EVENT_DELAY))) { + throw new ValidationException( + "Field: eventDate. Error: не может быть раньше, чем через " + + HOURS_EVENT_DELAY + " часа от текущего момента. Value: " + + newEventDto.getEventDate().format(DATA_TIME_FORMATTER) + ); + } + User user = userService.getUserById(userId); + Category category = categoryService.getCategoryById(newEventDto.getCategory()); + Event event = EventMapper.toEvent(newEventDto); + event.setInitiator(user); + event.setCategory(category); + Event savedEvent = eventRepository.save(event); + return EventMapper.toFullDto(savedEvent); + } + + /** + * Получение подробной информации о событии инициатором + * + * @param eventId - идентификатор события + * @param userId - идентификатор инициатора + * @return - объект события + */ + @Override + public EventFullDto getEventById(Integer eventId, Integer userId) { + Event event = findEventById(eventId); + if (!event.getInitiator().getId().equals(userId)) { + throw new ValidationException("Пользователь id=" + userId + + " не является инициатором события id=" + eventId); + } + event.setConfirmedRequests(requestRepository.getCountConfirmedRequestsByEventId(eventId)); + event.setViews(statsClient.getEventViews(eventId, true)); + return EventMapper.toFullDto(event); + } + + /** + * Поиск событий по идентификатору инициатора + * + * @param userId - идентификатор инициатора + * @param from - с какого события отображать список + * @param size - размер списка + * @return - список событий + */ + @Override + public List getEventsByUserId(Integer userId, Integer from, Integer size) { + List events = eventRepository.findEventsByInitiator_Id(userId); + updateViwesAndRequests(events); + return events.stream() + .skip(from) + .limit(size) + .map(EventMapper::toShortDto) + .toList(); + } + + /** + * Заполняем объекты списка событий сведениями о просмотрах и подтвержденных заявках + * + * @param events - список событий + */ + private void updateViwesAndRequests(List events) { + TreeMap eventMap = new TreeMap<>(); + List eventUris = new ArrayList<>(); + for (Event event : events) { + eventMap.put(event.getId(), event); + eventUris.add(String.format("/events/%d", event.getId())); + } + // заполняем количество заявок + List counts = + requestRepository.getCountConfirmedRequests(eventMap.keySet().stream().toList()); + for (EventConfirmedRequestCount count : counts) { + Integer eventId = count.getEventId(); + eventMap.get(eventId).setConfirmedRequests(count.getConfirmedRequestCount().intValue()); + } + // заполняем количество просмотров + List statsDtos = statsClient.getEventViewsByUris(eventUris, true); + for (StatsDto dto : statsDtos) { + Integer eventId = Integer.parseInt(dto.getUri().split("/")[2]); + eventMap.get(eventId).setViews(dto.getHits()); + } + } + + /** + * Обновление события инициатором + * + * @param eventId - идентификатор события + * @param eventDto - объект с обновляемыми данными + * @param userId - идентификатор инициатора + * @return - обновленный объект события + */ + @Override + public EventFullDto patchEvent(Integer eventId, @Validated UpdateEventUserRequest eventDto, Integer userId) { + Event event = eventRepository.findById(eventId) + .orElseThrow(() -> + new NotFoundException("Не найдено событие id=" + eventId)); + if (!event.getInitiator().getId().equals(userId)) { + throw new DataConflictException("Пользователь id=" + userId + + " не является инициатором события id=" + eventId); + } + if (event.getEventDate().isBefore(LocalDateTime.now().plusHours(HOURS_EVENT_DELAY))) { + throw new ValidationException( + "Field: eventDate. Error: не может быть раньше, чем через " + + HOURS_EVENT_DELAY + " часа от текущего момента. Value: " + + event.getEventDate().format(DATA_TIME_FORMATTER) + ); + } + if (event.getState().equals(EventState.PUBLISHED)) { + throw new DataConflictException( + "Field: event.state. Error: Недопустимый статус события для изменения." + + " Value: " + event.getState()); + } + if (eventDto.getAnnotation() != null) { + event.setAnnotation(eventDto.getAnnotation()); + } + if (eventDto.getCategory() != null) { + event.setCategory(categoryService.getCategoryById(eventDto.getCategory())); + } + if (eventDto.getDescription() != null) { + event.setDescription(eventDto.getDescription()); + } + if (eventDto.getEventDate() != null) { + if (eventDto.getEventDate().isBefore(LocalDateTime.now().plusHours(HOURS_EVENT_DELAY))) { + throw new ValidationException( + "Field: eventDate. Error: новое значение не может быть раньше, чем через " + + HOURS_EVENT_DELAY + " часа от текущего момента. Value: " + + eventDto.getEventDate().format(DATA_TIME_FORMATTER) + ); + } + event.setEventDate(eventDto.getEventDate()); + } + if (eventDto.getLocation() != null) { + event.setLat(eventDto.getLocation().getLat()); + event.setLon(eventDto.getLocation().getLon()); + } + if (eventDto.getPaid() != null) { + event.setPaid(eventDto.getPaid()); + } + if (eventDto.getParticipantLimit() != null) { + event.setParticipantLimit(eventDto.getParticipantLimit()); + } + if (eventDto.getRequestModeration() != null) { + event.setRequestModeration(eventDto.getRequestModeration()); + } + if (eventDto.getStateAction() != null) { + if (eventDto.getStateAction().equals(EventUserAction.CANCEL_REVIEW)) { + event.setState(EventState.CANCELED); + } else if (eventDto.getStateAction().equals(EventUserAction.SEND_TO_REVIEW)) { + event.setState(EventState.PENDING); + } + } + if (eventDto.getTitle() != null) { + event.setTitle(eventDto.getTitle()); + } + Event savedEvent = eventRepository.save(event); + savedEvent.setConfirmedRequests(requestRepository.getCountConfirmedRequestsByEventId(eventId)); + savedEvent.setViews(statsClient.getEventViews(eventId, true)); + return EventMapper.toFullDto(savedEvent); + } + + /** + * Обновление события администратором + * + * @param eventId - идентификатор события + * @param eventDto - объект с обновляемыми данными + * @return - обновленный объект события + */ + @Override + public EventFullDto adminUpdateEvent(Integer eventId, UpdateEventAdminRequest eventDto) { + Event event = eventRepository.findById(eventId) + .orElseThrow(() -> + new NotFoundException("Не найдено событие id=" + eventId)); + if (event.getEventDate() != null) { + if (event.getEventDate().isBefore(LocalDateTime.now().plusHours(HOURS_EVENT_DELAY))) { + throw new ValidationException( + "Field: eventDate. Error: не может быть раньше, чем через " + + HOURS_EVENT_DELAY + " часа от текущего момента. Value: " + + event.getEventDate().format(DATA_TIME_FORMATTER) + ); + } + } + if (eventDto.getAnnotation() != null) { + event.setAnnotation(eventDto.getAnnotation()); + } + if (eventDto.getCategory() != null) { + event.setCategory(categoryService.getCategoryById(eventDto.getCategory())); + } + if (eventDto.getDescription() != null) { + event.setDescription(eventDto.getDescription()); + } + if (eventDto.getEventDate() != null) { + if (eventDto.getEventDate().isBefore(LocalDateTime.now().plusHours(HOURS_EVENT_DELAY))) { + throw new ValidationException( + "Field: eventDate. Error: новое значение не может быть раньше, чем через " + + HOURS_EVENT_DELAY + " часа от текущего момента. Value: " + + eventDto.getEventDate().format(DATA_TIME_FORMATTER) + ); + } + event.setEventDate(eventDto.getEventDate()); + } + if (eventDto.getLocation() != null) { + event.setLat(eventDto.getLocation().getLat()); + event.setLon(eventDto.getLocation().getLon()); + } + if (eventDto.getPaid() != null) { + event.setPaid(eventDto.getPaid()); + } + if (eventDto.getParticipantLimit() != null) { + event.setParticipantLimit(eventDto.getParticipantLimit()); + } + if (eventDto.getRequestModeration() != null) { + event.setRequestModeration(eventDto.getRequestModeration()); + } + if (eventDto.getStateAction() != null) { + if (eventDto.getStateAction().equals(EventAdminAction.PUBLISH_EVENT)) { + if (!event.getState().equals(EventState.PENDING)) { + throw new DataConflictException( + "Field: stateAction. Error: " + + "Событие id=" + eventId + " должно быть в состоянии ожидания публикации." + + " Value: " + eventDto.getStateAction() + ); + } + event.setState(EventState.PUBLISHED); + event.setPublishedOn(LocalDateTime.now()); + } else if (eventDto.getStateAction().equals(EventAdminAction.REJECT_EVENT)) { + if (event.getState().equals(EventState.PUBLISHED)) { + throw new DataConflictException( + "Field: stateAction. Error: " + + "Нельзя удалить опубликованное событие id=" + eventId + + " Value: " + eventDto.getStateAction() + ); + } + event.setState(EventState.REJECTED); + } else { + throw new ValidationException( + "Field: stateAction. Error: " + + "Указано непредусмотренное действие. " + + " Value: " + eventDto.getStateAction()); + } + } + if (eventDto.getTitle() != null) { + event.setTitle(eventDto.getTitle()); + } + Event savedEvent = eventRepository.save(event); + savedEvent.setConfirmedRequests(requestRepository.getCountConfirmedRequestsByEventId(eventId)); + savedEvent.setViews(statsClient.getEventViews(eventId, true)); + return EventMapper.toFullDto(savedEvent); + } + + @Override + public Event findEventById(Integer eventId) { + Event event = eventRepository.findById(eventId) + .orElseThrow(() -> + new NotFoundException("Не найдено событие id=" + eventId)); + event.setConfirmedRequests(requestRepository.getCountConfirmedRequestsByEventId(eventId)); + event.setViews(statsClient.getEventViews(eventId, true)); + return event; + } + + /** + * Поиск событий пользователем + */ + @Override + public List findEventsByParametrs(String text, List categories, + Boolean paid, String rangeStart, String rangeEnd, + Boolean onlyAvailable, String sort, Integer from, Integer size) { + LocalDateTime startDate = null; + LocalDateTime endDate = null; + + if (rangeStart != null && !rangeStart.isEmpty()) { + try { + startDate = LocalDateTime.parse(rangeStart, DATA_TIME_FORMATTER); + } catch (DateTimeParseException e) { + throw new ValidationException("Некорректный формат времени. " + e.getMessage()); + } + } + if (rangeEnd != null && !rangeEnd.isEmpty()) { + try { + endDate = LocalDateTime.parse(rangeEnd, DATA_TIME_FORMATTER); + } catch (DateTimeParseException e) { + throw new ValidationException("Некорректный формат времени. " + e.getMessage()); + } + } + if (startDate != null && endDate != null) { + if (startDate.isAfter(endDate)) { + throw new BadRequestException( + "Parameter: rangeStart, rangeEnd. " + + "Error: Введен некорректный интервал времени." + + ". Value: " + startDate.format(DATA_TIME_FORMATTER) + + ", " + endDate.format(DATA_TIME_FORMATTER) + ); + } + } + // если в запросе не указан диапазон дат [rangeStart-rangeEnd], + // то нужно выгружать события, которые произойдут позже текущей даты и времени + if (startDate == null && endDate == null) { + startDate = LocalDateTime.now(); + } + + Specification spec = Specification.where(null); + // Задаем спецификации для поиска событий + // ...поиск событий по тексту в аннотации и подробном описании события + if (text != null) { + spec = spec.and(EventSpecification.annotetionContains(text)); + spec = spec.or(EventSpecification.descriptionContains(text)); + } + // ... поиск по списку идентификаторов категорий + if (categories != null) { + spec = spec.and(EventSpecification.categoryIn(categories)); + } + // ... поиск платных или бесплатных событий + if (paid != null) { + spec = spec.and(EventSpecification.paidEqual(paid)); + } + // Поиск по date события + if (startDate != null) { + spec = spec.and(EventSpecification.eventDateAfter(startDate)); + } + if (endDate != null) { + spec = spec.and(EventSpecification.eventDateBefore(endDate)); + } + + List events = eventRepository.findAll(spec, + Sort.by("eventDate")); + if (events.isEmpty()) { + return List.of(); + } + updateViwesAndRequests(events); + List eventDtos; + if (onlyAvailable) { + // Фильтруем события у которых не исчерпано количество заявок + eventDtos = events.stream() + .filter(event -> event.getParticipantLimit() != 0 + && event.getConfirmedRequests() < event.getParticipantLimit()) + .map(EventMapper::toShortDto) + .toList(); + } else { + eventDtos = events.stream().map(EventMapper::toShortDto).toList(); + } + + if (sort.equalsIgnoreCase("VIEWS")) { + return eventDtos.stream() + .sorted(Comparator.comparing(EventShortDto::getViews).reversed()) + .skip(from).limit(size).toList(); + } + return eventDtos.stream().skip(from).limit(size).toList(); + } + + /** + * Поиск событий администратором + */ + @Override + public List findEventsByAdmin(List states, + List users, + List categories, + String rangeStart, + String rangeEnd, + Integer from, + Integer size) { + LocalDateTime startDate = null; + LocalDateTime endDate = null; + try { + if (rangeStart != null && !rangeStart.isEmpty()) { + startDate = LocalDateTime.parse(rangeStart, DATA_TIME_FORMATTER); + } + if (rangeEnd != null && !rangeEnd.isEmpty()) { + endDate = LocalDateTime.parse(rangeEnd, DATA_TIME_FORMATTER); + } + if (startDate != null && endDate != null) { + startDate = LocalDateTime.now(); + } + } catch (DateTimeParseException e) { + throw new ValidationException("Некорректный формат времени. " + e.getMessage()); + } + + Specification spec = Specification.where(null); + // Задаем спецификации для поиска событий + // ...поиск событий по списку идентификаторов инициаторов + if (users != null) { + spec = spec.and(EventSpecification.eventInitiatorIdIn(users)); + } + // ... поиск по списку идентификаторов категорий + if (categories != null) { + spec = spec.and(EventSpecification.categoryIn(categories)); + } + // ... поиск по списку состояний + if (states != null) { + spec = spec.and(EventSpecification.eventStateIn(states)); + } + // Поиск по date события + if (startDate != null) { + spec = spec.and(EventSpecification.eventDateAfter(startDate)); + } + if (endDate != null) { + spec = spec.and(EventSpecification.eventDateBefore(endDate)); + } + + List events = eventRepository.findAll(spec, + Sort.by("eventDate")); + if (events.isEmpty()) { + return List.of(); + } + updateViwesAndRequests(events); + List eventDtos = events.stream() + .map(EventMapper::toFullDto) + .toList(); + return eventDtos.stream().skip(from).limit(size).toList(); + } + + /** + * Поиск событий по списку идентификаторов + */ + @Override + public List findEventsByIdIn(List eventIds) { + List events = eventRepository.findEventsByIdIn(eventIds); + if (events.isEmpty()) { + return List.of(); + } + updateViwesAndRequests(events); + return events; + } + +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/service/RequestService.java b/ewm-service/src/main/java/ru/practicum/evmsevice/service/RequestService.java new file mode 100644 index 0000000..cab08e2 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/service/RequestService.java @@ -0,0 +1,20 @@ +package ru.practicum.evmsevice.service; + +import ru.practicum.evmsevice.dto.RequestDto; +import ru.practicum.evmsevice.dto.RequestGroupDto; +import ru.practicum.evmsevice.dto.RequestUpdateDto; +import ru.practicum.evmsevice.model.Request; + +import java.util.List; + +public interface RequestService { + Request createRequest(Integer userId, Integer eventId); + + List getRequestsByUserId(Integer userId); + + Request canceledRequest(Integer userId, Integer requestId); + + List getRequestsByEventId(Integer userId, Integer eventId); + + RequestGroupDto updateRequestsStatus(Integer userId, Integer eventId, RequestUpdateDto requestUpdateDto); +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/service/RequestServiceImpl.java b/ewm-service/src/main/java/ru/practicum/evmsevice/service/RequestServiceImpl.java new file mode 100644 index 0000000..e3b69a8 --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/service/RequestServiceImpl.java @@ -0,0 +1,176 @@ +package ru.practicum.evmsevice.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import ru.practicum.evmsevice.dto.RequestDto; +import ru.practicum.evmsevice.dto.RequestGroupDto; +import ru.practicum.evmsevice.dto.RequestUpdateDto; +import ru.practicum.evmsevice.enums.EventState; +import ru.practicum.evmsevice.enums.RequestStatus; +import ru.practicum.evmsevice.exception.DataConflictException; +import ru.practicum.evmsevice.exception.NotFoundException; +import ru.practicum.evmsevice.exception.ValidationException; +import ru.practicum.evmsevice.mapper.RequestMapper; +import ru.practicum.evmsevice.model.Event; +import ru.practicum.evmsevice.model.Request; +import ru.practicum.evmsevice.model.User; +import ru.practicum.evmsevice.repository.RequestRepository; + +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.List; + +@Service +@Transactional +@RequiredArgsConstructor +public class RequestServiceImpl implements RequestService { + private final RequestRepository requestRepository; + private final EventService eventService; + private final UserService userService; + + @Override + public Request createRequest(Integer userId, Integer eventId) { + Event event = eventService.findEventById(eventId); + if (event.getInitiator().getId().equals(userId)) { + throw new DataConflictException( + "Field: event.initiator_id. Error: " + + "Инициатор события не может добавить запрос на участие в своём событии. " + + "Value: " + event.getInitiator().getId() + ); + } + if (!event.getState().equals(EventState.PUBLISHED)) { + throw new DataConflictException( + "Field: event.state. Error: " + + "Нельзя участвовать в неопубликованном событии. " + + "Value: " + event.getState() + ); + } + Integer confirmedRequests = event.getConfirmedRequests(); + if (confirmedRequests != null && event.getParticipantLimit() > 0) { + if (confirmedRequests.equals(event.getParticipantLimit())) { + throw new DataConflictException( + "Field: event.state. Error: " + + "У события достигнут лимит запросов на участие. " + + "Value: " + confirmedRequests + ); + } + } + Request request = new Request(); + User user = userService.getUserById(userId); + request.setRequester(user); + request.setEvent(event); + request.setStatus(RequestStatus.PENDING); + if (!event.getRequestModeration() || event.getParticipantLimit() == 0) { + request.setStatus(RequestStatus.CONFIRMED); + } + request.setCreated(LocalDateTime.now()); + return requestRepository.save(request); + } + + @Override + @Transactional(readOnly = true) + public List getRequestsByUserId(Integer userId) { + List requests = requestRepository.findAllByRequester_Id(userId); + if (requests.isEmpty()) { + return List.of(); + } + return requests.stream().map(RequestMapper::toRequestDto).toList(); + } + + @Override + public Request canceledRequest(Integer userId, Integer requestId) { + Request request = requestRepository.findById(requestId) + .orElseThrow(() -> + new NotFoundException("Не найден запрос id=" + requestId)); + if (!request.getRequester().getId().equals(userId)) { + throw new ValidationException( + "Field: request.requester.id. Error: " + + "Нельзя отменить чужой запрос. " + + "Value: " + request.getRequester().getId() + ); + } + request.setStatus(RequestStatus.CANCELED); + return requestRepository.save(request); + } + + /** + * Метод изменения поиска запросов к событию + */ + @Override + public List getRequestsByEventId(Integer userId, Integer eventId) { + Event event = eventService.findEventById(eventId); + if (!event.getInitiator().getId().equals(userId)) { + throw new ValidationException( + "Field: event.initiator_id. " + + "Error: пользователь id=" + userId + " не является инициатором события id=" + eventId + + ". Value: " + event.getInitiator().getId() + ); + } + return requestRepository.findAllByEvent_Id(eventId); + } + + /** + * Метод изменения статуса запросов + */ + @Override + public RequestGroupDto updateRequestsStatus(Integer userId, Integer eventId, RequestUpdateDto requestUpdateDto) { + Event event = eventService.findEventById(eventId); + if (!event.getInitiator().getId().equals(userId)) { + throw new ValidationException( + "Field: event.initiator_id. Error: " + + "Пользователь id=" + userId + " не является инициатором события id=" + eventId + + ". Value: " + event.getInitiator().getId() + ); + } + + // ...нельзя подтвердить заявку, если уже достигнут лимит по заявкам на данное событие + // (Ожидается код ошибки 409) + Integer confirmedRequests = requestRepository.getCountConfirmedRequestsByEventId(eventId); + if ((event.getParticipantLimit() > 0) + && event.getParticipantLimit().equals(confirmedRequests)) { + throw new DataConflictException( + "Field: event.confirmedRequests. " + + "Error: Достигнуто максимальное количество заявок для события id=" + eventId + + ". Value: " + confirmedRequests + ); + } + + RequestGroupDto requestGroupDto = new RequestGroupDto(); + List requestIds = requestUpdateDto.getRequestIds(); + if (requestIds.isEmpty()) { + return requestGroupDto; + } + Collections.sort(requestIds); + RequestStatus status = requestUpdateDto.getStatus(); + + // Проверяем заявки из списка + for (Integer requestId : requestIds) { + Request request = requestRepository.findById(requestId) + .orElseThrow(() -> new NotFoundException("Не найдена заявка id=" + requestId)); + // ... статус можно изменить только у заявок, находящихся в состоянии ожидания + // (Ожидается код ошибки 409) + if (!request.getStatus().equals(RequestStatus.PENDING)) { + throw new DataConflictException( + "Field: request.status. " + + "Error: недопустимый статус заявки id=" + requestId + + ". Value: " + request.getStatus() + ); + } + // ... если при подтверждении данной заявки, лимит заявок для события исчерпан, + // то все неподтверждённые заявки необходимо отклонить + if (confirmedRequests.equals(event.getParticipantLimit())) { + status = RequestStatus.REJECTED; + } + request.setStatus(status); + Request savedRequest = requestRepository.save(request); + if (savedRequest.getStatus().equals(RequestStatus.CONFIRMED)) { + requestGroupDto.getConfirmedRequests().add(RequestMapper.toRequestDto(savedRequest)); + confirmedRequests++; + } else if (savedRequest.getStatus().equals(RequestStatus.REJECTED)) { + requestGroupDto.getRejectedRequests().add(RequestMapper.toRequestDto(savedRequest)); + } + } + return requestGroupDto; + } +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/service/UserService.java b/ewm-service/src/main/java/ru/practicum/evmsevice/service/UserService.java new file mode 100644 index 0000000..15ae76e --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/service/UserService.java @@ -0,0 +1,17 @@ +package ru.practicum.evmsevice.service; + +import ru.practicum.evmsevice.model.User; + +import java.util.List; + +public interface UserService { + List getUsers(); + + List getUsers(List ids); + + User getUserById(Integer id); + + User addUser(User user); + + void deleteUser(Integer id); +} diff --git a/ewm-service/src/main/java/ru/practicum/evmsevice/service/UserServiceImpl.java b/ewm-service/src/main/java/ru/practicum/evmsevice/service/UserServiceImpl.java new file mode 100644 index 0000000..cce780f --- /dev/null +++ b/ewm-service/src/main/java/ru/practicum/evmsevice/service/UserServiceImpl.java @@ -0,0 +1,51 @@ +package ru.practicum.evmsevice.service; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import ru.practicum.evmsevice.exception.NotFoundException; +import ru.practicum.evmsevice.model.User; +import ru.practicum.evmsevice.repository.UserRepository; + +import java.util.List; + +@Service +@Transactional +public class UserServiceImpl implements UserService { + private final UserRepository userRepository; + + public UserServiceImpl(UserRepository userRepository) { + this.userRepository = userRepository; + } + + @Override + public User addUser(User user) { + User savedUser = userRepository.save(user); + return savedUser; + } + + @Override + public List getUsers() { + return userRepository.findAll(); + } + + @Override + public List getUsers(List ids) { + return userRepository.findAllById(ids); + } + + @Override + public User getUserById(Integer id) { + User user = userRepository.findById(id) + .orElseThrow(() -> + new NotFoundException("Не найден пользователь id=" + id)); + return user; + } + + @Override + public void deleteUser(Integer id) { + userRepository.findById(id) + .orElseThrow(() -> + new NotFoundException("Не найден пользователь id=" + id)); + userRepository.deleteById(id); + } +} diff --git a/ewm-service/src/main/resources/application.properties b/ewm-service/src/main/resources/application.properties index 39a76d7..4781508 100644 --- a/ewm-service/src/main/resources/application.properties +++ b/ewm-service/src/main/resources/application.properties @@ -1,2 +1,9 @@ server.port=8080 +spring.sql.init.mode=always +spring.jackson.date-format=yyyy-MM-dd HH:mm:ss +spring.datasource.driverClassName=org.postgresql.Driver +spring.datasource.url=jdbc:postgresql://192.168.0.102:5434/ewmdb +spring.datasource.username=ewmdb +spring.datasource.password=ewmdb +spring.application.name=ewm-service statserver.url=http://localhost:9090 \ No newline at end of file diff --git a/ewm-service/src/main/resources/schema.sql b/ewm-service/src/main/resources/schema.sql new file mode 100644 index 0000000..8c871d8 --- /dev/null +++ b/ewm-service/src/main/resources/schema.sql @@ -0,0 +1,70 @@ +CREATE TABLE IF NOT EXISTS categories +( + id INTEGER GENERATED BY DEFAULT AS IDENTITY NOT NULL, + name VARCHAR(128) NOT NULL, + CONSTRAINT pk_category PRIMARY KEY (id), + CONSTRAINT UQ_CATEGORY UNIQUE (name) +); + +CREATE TABLE IF NOT EXISTS users +( + id INTEGER GENERATED BY DEFAULT AS IDENTITY NOT NULL, + name VARCHAR(255) NOT NULL, + email VARCHAR(255) NOT NULL, + CONSTRAINT pk_user PRIMARY KEY (id), + CONSTRAINT UQ_USER_EMAIL UNIQUE (email) +); + +CREATE TABLE IF NOT EXISTS events +( + id INTEGER GENERATED BY DEFAULT AS IDENTITY NOT NULL, + annotation VARCHAR(2000), + category_id INTEGER, + createdOn TIMESTAMP WITHOUT TIME ZONE, + description VARCHAR(7000), + eventDate TIMESTAMP WITHOUT TIME ZONE, + initiator_id INTEGER NOT NULL, + lat FLOAT, + lon FLOAT, + paid BOOLEAN, + participantLimit INTEGER, + publishedOn TIMESTAMP WITHOUT TIME ZONE, + requestModeration BOOLEAN, + state VARCHAR(32), + title VARCHAR(128), + CONSTRAINT pk_event PRIMARY KEY (id), + CONSTRAINT fk_events_to_users FOREIGN KEY (initiator_id) REFERENCES users (id), + CONSTRAINT fk_events_to_categories FOREIGN KEY (category_id) REFERENCES categories (id) +); + +CREATE TABLE IF NOT EXISTS requests +( + id INTEGER GENERATED BY DEFAULT AS IDENTITY NOT NULL, + requester_id INTEGER NOT NULL, + event_id INTEGER NOT NULL, + status VARCHAR(32), + created TIMESTAMP WITHOUT TIME ZONE, + CONSTRAINT pk_request PRIMARY KEY (id), + CONSTRAINT fk_requests_to_users FOREIGN KEY (requester_id) REFERENCES users (id), + CONSTRAINT fk_requests_to_events FOREIGN KEY (event_id) REFERENCES events (id), + CONSTRAINT unique_requester_event UNIQUE (requester_id, event_id) +); + +CREATE TABLE IF NOT EXISTS compilations +( + id INTEGER GENERATED BY DEFAULT AS IDENTITY NOT NULL, + title VARCHAR(128), + pinned BOOLEAN, + CONSTRAINT pk_compilation PRIMARY KEY (id), + CONSTRAINT UQ_COMPILATION_TITLE UNIQUE (title) +); + + +CREATE TABLE IF NOT EXISTS eventlinks +( + event_id INTEGER NOT NULL, + compilation_id INTEGER NOT NULL, + CONSTRAINT pk_eventlinks PRIMARY KEY (event_id, compilation_id), + CONSTRAINT fk_links_to_events FOREIGN KEY (event_id) REFERENCES events (id), + CONSTRAINT fk_links_to_compilations FOREIGN KEY (compilation_id) REFERENCES compilations (id) +); diff --git a/stats-server/stat-client/src/main/java/ru/practicum/statclient/BaseClient.java b/stats-server/stat-client/src/main/java/ru/practicum/statclient/BaseClient.java index 28ce7fc..be8a867 100644 --- a/stats-server/stat-client/src/main/java/ru/practicum/statclient/BaseClient.java +++ b/stats-server/stat-client/src/main/java/ru/practicum/statclient/BaseClient.java @@ -1,11 +1,14 @@ package ru.practicum.statclient; +import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.HttpEntity; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.web.client.HttpStatusCodeException; import org.springframework.web.client.RestTemplate; +import ru.practicum.statdto.StatsDto; +import java.util.List; import java.util.Map; public class BaseClient { @@ -52,7 +55,11 @@ protected ResponseEntity makeAndSendRequest(HttpMethod method, stringParametrs.append(key); stringParametrs.append("}&"); } - serverResponse = rest.exchange(stringParametrs.toString(), method, requestEntity, Object.class, parameters); + serverResponse = rest.exchange(stringParametrs.toString(), + method, + requestEntity, + Object.class, + parameters); } else { serverResponse = rest.exchange(path, method, requestEntity, Object.class); } @@ -61,4 +68,29 @@ protected ResponseEntity makeAndSendRequest(HttpMethod method, } return prepareClientResponse(serverResponse); } + + protected List getStatsList(String path, + Map parameters) { + ResponseEntity> serverResponse = null; + try { + if (parameters != null) { + StringBuilder stringParametrs = new StringBuilder(path); + stringParametrs.append("?"); + for (String key : parameters.keySet()) { + stringParametrs.append(key); + stringParametrs.append("={"); + stringParametrs.append(key); + stringParametrs.append("}&"); + } + serverResponse = rest.exchange(stringParametrs.toString(), HttpMethod.GET, null, + new ParameterizedTypeReference>() { + }, parameters); + } + } catch (Exception e) { + e.printStackTrace(); + return List.of(); + } + List dtos = serverResponse.getBody(); + return dtos; + } } diff --git a/stats-server/stat-svc/src/main/java/ru/practicum/statsvc/controller/StatController.java b/stats-server/stat-svc/src/main/java/ru/practicum/statsvc/controller/StatController.java index 98bb153..908889f 100644 --- a/stats-server/stat-svc/src/main/java/ru/practicum/statsvc/controller/StatController.java +++ b/stats-server/stat-svc/src/main/java/ru/practicum/statsvc/controller/StatController.java @@ -15,7 +15,6 @@ @RestController @RequestMapping public class StatController { - private final StatService statService; @PostMapping("/hit") diff --git a/stats-server/stat-svc/src/main/java/ru/practicum/statsvc/model/ViewStats.java b/stats-server/stat-svc/src/main/java/ru/practicum/statsvc/model/ViewStats.java index 343b3eb..c57f911 100644 --- a/stats-server/stat-svc/src/main/java/ru/practicum/statsvc/model/ViewStats.java +++ b/stats-server/stat-svc/src/main/java/ru/practicum/statsvc/model/ViewStats.java @@ -10,7 +10,7 @@ @AllArgsConstructor @NoArgsConstructor public class ViewStats { - String app; - String uri; - Integer hits; + private String app; + private String uri; + private Integer hits; } diff --git a/stats-server/stat-svc/src/main/java/ru/practicum/statsvc/repository/StatDbStorage.java b/stats-server/stat-svc/src/main/java/ru/practicum/statsvc/repository/StatDbStorage.java index e6ff0be..a56a5c2 100644 --- a/stats-server/stat-svc/src/main/java/ru/practicum/statsvc/repository/StatDbStorage.java +++ b/stats-server/stat-svc/src/main/java/ru/practicum/statsvc/repository/StatDbStorage.java @@ -31,7 +31,7 @@ public StatDbStorage(NamedParameterJdbcTemplate jdbc) { } @Override - public EndpointHit addHit(EndpointHit hit) { + public void addHit(EndpointHit hit) { GeneratedKeyHolder generatedKeyHolder = new GeneratedKeyHolder(); try { jdbc.update(SQL_INSERT_HIT, @@ -49,55 +49,53 @@ public EndpointHit addHit(EndpointHit hit) { // получаем идентификатор final Integer hitId = generatedKeyHolder.getKey().intValue(); hit.setId(hitId); - - return hit; } @Override public List getViewStats(LocalDateTime start, LocalDateTime end, List uris, Boolean unique, Integer size) { StringBuilder sql = new StringBuilder(); - sql.append("SELECT app, uri, count(ip) as hits FROM"); + sql.append("SELECT e.app, e.uri, count(e.ip) as hits FROM"); if (unique) { - sql.append(" (SELECT DISTINCT ON (ip) app, uri, ip, timestamp FROM endpointhits)"); + sql.append(" (SELECT DISTINCT ON (ip, uri) app, uri, ip, timestamp FROM endpointhits) AS e"); } else { - sql.append(" endpointhits"); + sql.append(" endpointhits AS e"); } MapSqlParameterSource parameters = new MapSqlParameterSource(); Boolean whereFlag = false; if (uris != null && !uris.isEmpty()) { - sql.append(" WHERE uri IN (:uris)"); - parameters.addValue("uris", uris); - whereFlag = true; + if (!uris.get(0).equalsIgnoreCase("/events")) { + sql.append(" WHERE e.uri IN (:uris)"); + parameters.addValue("uris", uris); + whereFlag = true; + } } if (start != null) { if (whereFlag) { - sql.append(" AND timestamp >= :start"); + sql.append(" AND e.timestamp >= :start"); } else { - sql.append(" WHERE timestamp >= :start"); + sql.append(" WHERE e.timestamp >= :start"); whereFlag = true; } parameters.addValue("start", start); } if (end != null) { if (whereFlag) { - sql.append(" AND timestamp < :end"); + sql.append(" AND e.timestamp < :end"); } else { - sql.append(" WHERE timestamp < :end"); + sql.append(" WHERE e.timestamp < :end"); } parameters.addValue("end", end); } - sql.append(" GROUP BY uri, app ORDER BY hits DESC"); + sql.append(" GROUP BY e.uri, e.app ORDER BY hits DESC"); if (size != null) { parameters.addValue("size", size); sql.append(" LIMIT :size"); } try { - List viewStatsList = jdbc.query(sql.toString(), - parameters, + return jdbc.query(sql.toString(), parameters, new ViewStatsRowMapper()); - return viewStatsList; } catch (EmptyResultDataAccessException ignored) { return List.of(); } diff --git a/stats-server/stat-svc/src/main/java/ru/practicum/statsvc/repository/StatStorage.java b/stats-server/stat-svc/src/main/java/ru/practicum/statsvc/repository/StatStorage.java index 573ee32..886ebd3 100644 --- a/stats-server/stat-svc/src/main/java/ru/practicum/statsvc/repository/StatStorage.java +++ b/stats-server/stat-svc/src/main/java/ru/practicum/statsvc/repository/StatStorage.java @@ -7,7 +7,7 @@ import java.util.List; public interface StatStorage { - EndpointHit addHit(EndpointHit hit); + void addHit(EndpointHit hit); List getViewStats(LocalDateTime start, LocalDateTime end, List uris, Boolean unique, Integer size); } diff --git a/stats-server/stat-svc/src/main/java/ru/practicum/statsvc/service/StatServiceImpl.java b/stats-server/stat-svc/src/main/java/ru/practicum/statsvc/service/StatServiceImpl.java index dbb381e..345c09a 100644 --- a/stats-server/stat-svc/src/main/java/ru/practicum/statsvc/service/StatServiceImpl.java +++ b/stats-server/stat-svc/src/main/java/ru/practicum/statsvc/service/StatServiceImpl.java @@ -1,6 +1,7 @@ package ru.practicum.statsvc.service; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import ru.practicum.statdto.HitDto; import ru.practicum.statdto.StatsDto; import ru.practicum.statsvc.exception.ValidationException; @@ -14,6 +15,7 @@ import java.util.List; @Service +@Transactional public class StatServiceImpl implements StatService { private static final DateTimeFormatter DATA_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); private final StatDbStorage storage; @@ -38,9 +40,15 @@ public List getStats(String startTxt, try { if (startTxt != null && !startTxt.isEmpty()) { start = LocalDateTime.parse(startTxt, DATA_TIME_FORMATTER); + if (endTxt == null) { + throw new ValidationException("Отсутствует окончание временного периода."); + } } if (endTxt != null && !endTxt.isEmpty()) { end = LocalDateTime.parse(endTxt, DATA_TIME_FORMATTER); + if (start == null) { + throw new ValidationException("Отсутствует начало временного периода."); + } } } catch (DateTimeParseException e) { throw new ValidationException("Некорректный формат времени. " + e.getMessage()); diff --git a/stats-server/stat-svc/src/main/resources/application.properties b/stats-server/stat-svc/src/main/resources/application.properties index a69735a..7d9e7f5 100644 --- a/stats-server/stat-svc/src/main/resources/application.properties +++ b/stats-server/stat-svc/src/main/resources/application.properties @@ -1,14 +1,9 @@ server.port=9090 spring.sql.init.mode=always spring.jackson.date-format=yyyy-MM-dd HH:mm:ss - spring.datasource.driverClassName=org.postgresql.Driver spring.datasource.url=jdbc:postgresql://192.168.0.102:5432/statdb spring.datasource.username=statdb spring.datasource.password=statdb -#spring.datasource.driverClassName=org.h2.Driver -#spring.datasource.url=jdbc:h2:mem:statdb -#spring.datasource.username=statdb -#spring.datasource.password=statdb