diff --git a/.github/scripts/docker-compose.yml b/.github/scripts/docker-compose.yml
index d27c9cb8..1c9bd54e 100644
--- a/.github/scripts/docker-compose.yml
+++ b/.github/scripts/docker-compose.yml
@@ -26,6 +26,7 @@ services:
LIVE_POLL_MYSQL_USER: livepoll
LIVE_POLL_MYSQL_PASSWORD: M3uBcPLbM7RmgX4C3wAUej6WPzq886
LIVE_POLL_DEV_URL: localhost:4200
+ LIVE_POLL_FRONTEND_URL: localhost:4200
LIVE_POLL_SERVER_URL: localhost:8080
LIVE_POLL_MAIL_HOST: ${LIVE_POLL_MAIL_HOST}
LIVE_POLL_MAIL_PORT: ${LIVE_POLL_MAIL_PORT}
@@ -41,12 +42,6 @@ services:
depends_on:
db:
condition: service_healthy
- healthcheck:
- test: "curl --fail --silent localhost:8080/actuator/health | grep UP || exit 1"
- interval: 10s
- timeout: 5s
- retries: 10
- start_period: 40s
networks:
app-db: {}
\ No newline at end of file
diff --git a/.github/workflows/ci-with-docker-develop.yml b/.github/workflows/ci-with-docker-develop.yml
index 8fb07fa4..08d54c94 100644
--- a/.github/workflows/ci-with-docker-develop.yml
+++ b/.github/workflows/ci-with-docker-develop.yml
@@ -27,6 +27,7 @@ jobs:
LIVE_POLL_MYSQL_USER: ${{ secrets.API_LIVE_POLL_MYSQL_USER }}
LIVE_POLL_MYSQL_PASSWORD: ${{ secrets.API_LIVE_POLL_MYSQL_PASSWORD }}
LIVE_POLL_DEV_URL: ${{ secrets.API_LIVE_POLL_DEV_URL }}
+ LIVE_POLL_FRONTEND_URL: ${{ secrets.API_LIVE_POLL_FRONTEND_URL }}
LIVE_POLL_SERVER_URL: ${{ secrets.API_LIVE_POLL_SERVER_URL }}
LIVE_POLL_MAIL_HOST: ${{ secrets.API_LIVE_POLL_MAIL_HOST }}
LIVE_POLL_MAIL_PORT: ${{ secrets.API_LIVE_POLL_MAIL_PORT }}
diff --git a/.github/workflows/ci-with-docker.yml b/.github/workflows/ci-with-docker.yml
index aeeb2425..22f906c8 100644
--- a/.github/workflows/ci-with-docker.yml
+++ b/.github/workflows/ci-with-docker.yml
@@ -29,6 +29,7 @@ jobs:
LIVE_POLL_MYSQL_USER: ${{ secrets.API_LIVE_POLL_MYSQL_USER }}
LIVE_POLL_MYSQL_PASSWORD: ${{ secrets.API_LIVE_POLL_MYSQL_PASSWORD }}
LIVE_POLL_DEV_URL: ${{ secrets.API_LIVE_POLL_DEV_URL }}
+ LIVE_POLL_FRONTEND_URL: ${{ secrets.API_LIVE_POLL_FRONTEND_URL }}
LIVE_POLL_SERVER_URL: ${{ secrets.API_LIVE_POLL_SERVER_URL }}
LIVE_POLL_MAIL_HOST: ${{ secrets.API_LIVE_POLL_MAIL_HOST }}
LIVE_POLL_MAIL_PORT: ${{ secrets.API_LIVE_POLL_MAIL_PORT }}
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 57f98c49..7d6263c4 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -29,6 +29,7 @@ jobs:
LIVE_POLL_MYSQL_USER: ${{ secrets.API_LIVE_POLL_MYSQL_USER }}
LIVE_POLL_MYSQL_PASSWORD: ${{ secrets.API_LIVE_POLL_MYSQL_PASSWORD }}
LIVE_POLL_DEV_URL: ${{ secrets.API_LIVE_POLL_DEV_URL }}
+ LIVE_POLL_FRONTEND_URL: ${{ secrets.API_LIVE_POLL_FRONTEND_URL }}
LIVE_POLL_SERVER_URL: ${{ secrets.API_LIVE_POLL_SERVER_URL }}
LIVE_POLL_MAIL_HOST: ${{ secrets.API_LIVE_POLL_MAIL_HOST }}
LIVE_POLL_MAIL_PORT: ${{ secrets.API_LIVE_POLL_MAIL_PORT }}
@@ -61,9 +62,12 @@ jobs:
- name: Install Newman
run: sudo npm i -g newman
-
- - name: API healthcheck
- run: curl -sSLf --retry-delay 5 --retry 5 --retry-connrefused --insecure http://example.org > /dev/null
+
+ - name: Test Docker Container listing
+ run: docker ps
+
+ - name: Wait for API to come online
+ run: sleep 20s
- name: Run Newman tests
- run: newman run ./postman/Livepoll.postman_collection.json --iteration-count 3 --folder "Integration Test" --insecure
\ No newline at end of file
+ run: newman run ./postman/Livepoll.postman_collection.json --iteration-count 3 --folder "Integration Test" --insecure
diff --git a/env/docker-compose.yml b/env/docker-compose.yml
index 0746c656..66d000e8 100644
--- a/env/docker-compose.yml
+++ b/env/docker-compose.yml
@@ -4,8 +4,8 @@ services:
image: mysql:8.0
container_name: live-poll-dev-environment-db-mysql
volumes:
- - ./volumes/mysql-data:/var/lib/mysql
- - ./volumes/mysql-logs:/var/log/mysql
+ - ~/volumes/mysql-data:/var/lib/mysql
+ - ~/volumes/mysql-logs:/var/log/mysql
networks:
- mysql-phpmyadmin
ports:
diff --git a/pom.xml b/pom.xml
index 8bb73542..0ff3a3c8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,12 +5,12 @@
org.springframework.boot
spring-boot-starter-parent
- 2.5.0
+ 2.5.1
de.live-poll
api
- 0.7.0
+ 0.8.0
Live-Poll
API Backend for Live-Poll
@@ -24,7 +24,7 @@
org.springframework.boot
spring-boot-starter-quartz
- 2.5.0
+ 2.5.1
diff --git a/postman/Livepoll.postman_collection.json b/postman/Livepoll.postman_collection.json
index 780494bf..ebd72d90 100644
--- a/postman/Livepoll.postman_collection.json
+++ b/postman/Livepoll.postman_collection.json
@@ -1,6 +1,6 @@
{
"info": {
- "_postman_id": "63c95483-1621-437e-8135-14b5c87cd4c8",
+ "_postman_id": "b83df12e-5482-4bc6-baa9-c95b38afdbb2",
"name": "Livepoll",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
@@ -947,11 +947,17 @@
" pm.response.to.have.status(201);\r",
"});\r",
"\r",
+ "const response = pm.response.json();\r",
+ "\r",
"pm.test(\"Multiple choice item is at position 1\", () => {\r",
- " const response = pm.response.json();\r",
" pm.expect(response.position).to.equal(1);\r",
"});\r",
"\r",
+ "pm.test(\"Answers are in correct order\", () => {\r",
+ " pm.expect(response.answers[0].selectionOption).to.equal(\"a orig\");\r",
+ " pm.expect(response.answers[1].selectionOption).to.equal(\"b orig\");\r",
+ "});\r",
+ "\r",
"pm.globals.set(\"poll-item-id-multiple-choice\", pm.response.json().itemId);\r",
"pm.globals.set(\"poll-item-id\", pm.response.json().itemId);\r",
""
@@ -971,7 +977,7 @@
],
"body": {
"mode": "raw",
- "raw": "{\r\n \"pollId\": \"{{poll-id}}\",\r\n \"question\": \"postman-multiplechoice-question\",\r\n \"selectionOptions\": [\"postman-multiplechoice-answer-1\", \"postman-multiplechoice-answer-2\"]\r\n}",
+ "raw": "{\r\n \"pollId\": \"{{poll-id}}\",\r\n \"question\": \"postman-multiplechoice-question\",\r\n \"selectionOptions\": [\"a orig\", \"b orig\"]\r\n}",
"options": {
"raw": {
"language": "json"
@@ -999,13 +1005,80 @@
"listen": "test",
"script": {
"exec": [
+ "// Async operations\r",
+ "// https://community.postman.com/t/async-operations/24314/3\r",
+ "\r",
+ "/**\r",
+ " * @private\r",
+ " * @description Internal function to run tasks in series\r",
+ " * \r",
+ " * @param {Array} tasks\r",
+ " * @param {Function} cb\r",
+ " * @param {Number} currOperation\r",
+ " * @param {Array} results\r",
+ " */\r",
+ "function _series (tasks, cb, currOperation = 0, results = []) {\r",
+ " // Bail-out condition\r",
+ " if (currOperation === tasks.length) {\r",
+ " return cb(null, results);\r",
+ " }\r",
+ "\r",
+ " if (typeof tasks[currOperation] !== 'function') {\r",
+ " return cb(new Error('asyncSeries: Please provide a function'));\r",
+ " }\r",
+ "\r",
+ " tasks[currOperation]((err, res) => {\r",
+ " if (err) {\r",
+ " return cb(err);\r",
+ " }\r",
+ "\r",
+ " results.push(res);\r",
+ "\r",
+ " // Recursively call the next task in series till we're done executing all the operations\r",
+ " return _series(tasks, cb, currOperation + 1, results);\r",
+ " });\r",
+ "}\r",
+ "\r",
+ "/**\r",
+ " * @description asyncSeries to execute requests in a series format\r",
+ " * \r",
+ " * @param {Array} tasks\r",
+ " * @param {Function} cb\r",
+ " */\r",
+ "function asyncSeries (tasks, cb = () => {}) {\r",
+ " return _series(tasks, cb);\r",
+ "}\r",
+ "\r",
+ "/////////////////////////////////////////////////////////////////////////////////////////////////////////\r",
+ "\r",
+ "\r",
"pm.test(\"Status code is 201\", function () {\r",
" pm.response.to.have.status(201);\r",
"});\r",
"\r",
- "pm.test(\"Quiz item is at position 2\", () => {\r",
- " const response = pm.response.json();\r",
- " pm.expect(response.position).to.equal(2);\r",
+ "const response = pm.response.json();\r",
+ "\r",
+ "asyncSeries([\r",
+ "\r",
+ " (cb) => pm.test(\"Quiz item is at position 2\", () => {\r",
+ " pm.expect(response.position).to.equal(2);\r",
+ "\r",
+ " cb(\"\", response);\r",
+ " }),\r",
+ "\r",
+ " (cb) => pm.test(\"Answers are in correct order and the first one is correct\", () => {\r",
+ " pm.expect(response.answers[0].selectionOption).to.equal(\"a orig\");\r",
+ " pm.expect(response.answers[0].isCorrect).to.be.true;\r",
+ " pm.expect(response.answers[1].selectionOption).to.equal(\"b orig\");\r",
+ " pm.expect(response.answers[1].isCorrect).to.be.false;\r",
+ " pm.expect(response.answers[2].selectionOption).to.equal(\"c orig\");\r",
+ " pm.expect(response.answers[2].isCorrect).to.be.false;\r",
+ "\r",
+ " cb(\"\", response);\r",
+ " })\r",
+ "\r",
+ "], (err, res) => {\r",
+ " console.log('Series operations resolved (Create Quiz Item)', err, res);\r",
"});\r",
"\r",
"pm.globals.set(\"poll-item-id-quiz\", pm.response.json().itemId);\r",
@@ -1013,6 +1086,15 @@
],
"type": "text/javascript"
}
+ },
+ {
+ "listen": "prerequest",
+ "script": {
+ "exec": [
+ ""
+ ],
+ "type": "text/javascript"
+ }
}
],
"request": {
@@ -1026,7 +1108,7 @@
],
"body": {
"mode": "raw",
- "raw": "{\r\n \"pollId\": \"{{poll-id}}\",\r\n \"question\": \"postman-quiz-question\",\r\n \"selectionOptions\": [ \"correct option\", \"wrong option\", \"wrong option\"]\r\n}",
+ "raw": "{\r\n \"pollId\": \"{{poll-id}}\",\r\n \"question\": \"postman-quiz-question\",\r\n \"selectionOptions\": [ \"a orig\", \"b orig\", \"c orig\"]\r\n}",
"options": {
"raw": {
"language": "json"
@@ -1324,6 +1406,53 @@
"listen": "test",
"script": {
"exec": [
+ "// Async operations\r",
+ "// https://community.postman.com/t/async-operations/24314/3\r",
+ "\r",
+ "/**\r",
+ " * @private\r",
+ " * @description Internal function to run tasks in series\r",
+ " * \r",
+ " * @param {Array} tasks\r",
+ " * @param {Function} cb\r",
+ " * @param {Number} currOperation\r",
+ " * @param {Array} results\r",
+ " */\r",
+ "function _series (tasks, cb, currOperation = 0, results = []) {\r",
+ " // Bail-out condition\r",
+ " if (currOperation === tasks.length) {\r",
+ " return cb(null, results);\r",
+ " }\r",
+ "\r",
+ " if (typeof tasks[currOperation] !== 'function') {\r",
+ " return cb(new Error('asyncSeries: Please provide a function'));\r",
+ " }\r",
+ "\r",
+ " tasks[currOperation]((err, res) => {\r",
+ " if (err) {\r",
+ " return cb(err);\r",
+ " }\r",
+ "\r",
+ " results.push(res);\r",
+ "\r",
+ " // Recursively call the next task in series till we're done executing all the operations\r",
+ " return _series(tasks, cb, currOperation + 1, results);\r",
+ " });\r",
+ "}\r",
+ "\r",
+ "/**\r",
+ " * @description asyncSeries to execute requests in a series format\r",
+ " * \r",
+ " * @param {Array} tasks\r",
+ " * @param {Function} cb\r",
+ " */\r",
+ "function asyncSeries (tasks, cb = () => {}) {\r",
+ " return _series(tasks, cb);\r",
+ "}\r",
+ "\r",
+ "/////////////////////////////////////////////////////////////////////////////////////////////////////////\r",
+ "\r",
+ "\r",
"const baseUrl = pm.environment.get('base-url');\r",
"const pollId = pm.globals.get('poll-id');\r",
"const cookies = pm.environment.get('cookies');\r",
@@ -1338,113 +1467,125 @@
" pm.response.to.have.status(200);\r",
"});\r",
"\r",
+ "asyncSeries([\r",
"\r",
- "// ---------------------- Position -------------------------------\r",
- "pm.test(\"Multiple choice item got moved from position 1 to position 3\", () => {\r",
- " pm.sendRequest({\r",
- " url: `${baseUrl}/v1/polls/${pollId}/poll-items`,\r",
- " method: 'GET',\r",
- " header: {\r",
- " 'Cookie': cookies\r",
- " }\r",
- " }, function (err, response) {\r",
- " response = response.json();\r",
+ " // ---------------------- Position -------------------------------\r",
+ " (cb) => pm.test(\"Multiple choice item got moved from position 1 to position 3\", () => {\r",
+ " pm.sendRequest({\r",
+ " url: `${baseUrl}/v1/polls/${pollId}/poll-items`,\r",
+ " method: 'GET',\r",
+ " header: {\r",
+ " 'Cookie': cookies\r",
+ " }\r",
+ " }, function (err, response) {\r",
+ " response = response.json();\r",
"\r",
- " // Multiple choice item got moved from position 1 to position 3\r",
- " pm.expect(response[0].itemId).to.equal(multipleChoiceId);\r",
- " pm.expect(response[0].position).to.equal(3);\r",
+ " // Multiple choice item got moved from position 1 to position 3\r",
+ " pm.expect(response[0].itemId).to.equal(multipleChoiceId);\r",
+ " pm.expect(response[0].position).to.equal(3);\r",
"\r",
- " pm.expect(response[1].itemId).to.equal(quizId);\r",
- " pm.expect(response[1].position).to.equal(1);\r",
+ " pm.expect(response[1].itemId).to.equal(quizId);\r",
+ " pm.expect(response[1].position).to.equal(1);\r",
"\r",
- " pm.expect(response[2].itemId).to.equal(openTextId);\r",
- " pm.expect(response[2].position).to.equal(2);\r",
- " });\r",
- "});\r",
+ " pm.expect(response[2].itemId).to.equal(openTextId);\r",
+ " pm.expect(response[2].position).to.equal(2);\r",
"\r",
+ " cb(err, response);\r",
+ " });\r",
+ " }),\r",
"\r",
- "// -------------------- Selection options -------------------------------\r",
- "pm.test(\"Selection options that already exist are kept\", () => {\r",
- " pm.sendRequest({\r",
- " url: `${baseUrl}/v1/poll-items/multiple-choice/${multipleChoiceId}`,\r",
- " method: 'PUT',\r",
- " header: {\r",
- " 'Cookie': cookies,\r",
- " 'Content-Type': 'application/json'\r",
- " },\r",
- " body: {\r",
- " mode: 'raw',\r",
- " raw: JSON.stringify({\r",
- " \"pollId\": `${pollId}`,\r",
- " \"question\": \"postman-multiplechoice-question-update\",\r",
- " \"position\": 3,\r",
- " \"selectionOptions\": [\"postman-multiplechoice-answer-1-update\", \"postman-multiplechoice-answer-2-update\"]\r",
- " })\r",
- " }\r",
- " }, function (err, response) {\r",
- " response = response.json();\r",
+ " // -------------------- Selection options -------------------------------\r",
+ " (cb) => pm.test(\"Selection options that already exist are kept\", () => {\r",
+ " pm.sendRequest({\r",
+ " url: `${baseUrl}/v1/poll-items/multiple-choice/${multipleChoiceId}`,\r",
+ " method: 'PUT',\r",
+ " header: {\r",
+ " 'Cookie': cookies,\r",
+ " 'Content-Type': 'application/json'\r",
+ " },\r",
+ " body: {\r",
+ " mode: 'raw',\r",
+ " raw: JSON.stringify({\r",
+ " \"pollId\": `${pollId}`,\r",
+ " \"question\": \"postman-multiplechoice-question-update\",\r",
+ " \"position\": 3,\r",
+ " \"selectionOptions\": [\"a\", \"b\"]\r",
+ " })\r",
+ " }\r",
+ " }, function (err, response) {\r",
+ " response = response.json();\r",
"\r",
- " // Selection option already existed and are kept\r",
- " pm.expect(response.answers[0].selectionOption).to.equal(\"postman-multiplechoice-answer-1-update\");\r",
- " pm.expect(response.answers[1].selectionOption).to.equal(\"postman-multiplechoice-answer-2-update\");\r",
- " // TODO: test in conjunction with websockets to make sure that answer count also stays the same\r",
- " });\r",
- "});\r",
+ " // Selection option already existed and are kept\r",
+ " pm.expect(response.answers[0].selectionOption).to.equal(\"a\");\r",
+ " pm.expect(response.answers[1].selectionOption).to.equal(\"b\");\r",
+ " // TODO: test in conjunction with websockets to make sure that answer count also stays the same\r",
"\r",
- "pm.test(\"New selection options are added (with initial answer count of 0)\", () => {\r",
- " pm.sendRequest({\r",
- " url: `${baseUrl}/v1/poll-items/multiple-choice/${multipleChoiceId}`,\r",
- " method: 'PUT',\r",
- " header: {\r",
- " 'Cookie': cookies,\r",
- " 'Content-Type': 'application/json'\r",
- " },\r",
- " body: {\r",
- " mode: 'raw',\r",
- " raw: JSON.stringify({\r",
- " \"pollId\": `${pollId}`,\r",
- " \"question\": \"postman-multiplechoice-question-update\",\r",
- " \"position\": 3,\r",
- " \"selectionOptions\": [\"new-option\", \"postman-multiplechoice-answer-1-update\", \"postman-multiplechoice-answer-2-update\"]\r",
- " })\r",
- " }\r",
- " }, function (err, response) {\r",
- " response = response.json();\r",
+ " cb(err, response);\r",
+ " });\r",
+ " }),\r",
"\r",
- " // New selection option is added at the end\r",
- " pm.expect(response.answers[0].selectionOption).to.equal(\"postman-multiplechoice-answer-1-update\");\r",
- " pm.expect(response.answers[1].selectionOption).to.equal(\"postman-multiplechoice-answer-2-update\");\r",
- " pm.expect(response.answers[2].selectionOption).to.equal(\"new-option\"); // was first item in the request body selectionOptions\r",
+ " (cb) => pm.test(\"New selection options are added (with initial answer count of 0) & order is guaranteed\", () => {\r",
+ " pm.sendRequest({\r",
+ " url: `${baseUrl}/v1/poll-items/multiple-choice/${multipleChoiceId}`,\r",
+ " method: 'PUT',\r",
+ " header: {\r",
+ " 'Cookie': cookies,\r",
+ " 'Content-Type': 'application/json'\r",
+ " },\r",
+ " body: {\r",
+ " mode: 'raw',\r",
+ " raw: JSON.stringify({\r",
+ " \"pollId\": `${pollId}`,\r",
+ " \"question\": \"postman-multiplechoice-question-update\",\r",
+ " \"position\": 3,\r",
+ " \"selectionOptions\": [\"c\", \"a\", \"b\"]\r",
+ " })\r",
+ " }\r",
+ " }, function (err, response) {\r",
+ " response = response.json();\r",
"\r",
- " // Initial answer count must equal 0\r",
- " pm.expect(response.answers[2].answerCount).to.equal(0)\r",
- " });\r",
- "});\r",
+ " // New selection option is added at the end\r",
+ " pm.expect(response.answers[0].selectionOption).to.equal(\"c\");\r",
+ " pm.expect(response.answers[1].selectionOption).to.equal(\"a\");\r",
+ " pm.expect(response.answers[2].selectionOption).to.equal(\"b\");\r",
"\r",
- "pm.test(\"Selection option that existed in database but not anymore in update is removed\", () => {\r",
- " pm.sendRequest({\r",
- " url: `${baseUrl}/v1/poll-items/multiple-choice/${multipleChoiceId}`,\r",
- " method: 'PUT',\r",
- " header: {\r",
- " 'Cookie': cookies,\r",
- " 'Content-Type': 'application/json'\r",
- " },\r",
- " body: {\r",
- " mode: 'raw',\r",
- " raw: JSON.stringify({\r",
- " \"pollId\": `${pollId}`,\r",
- " \"question\": \"postman-multiplechoice-question-update\",\r",
- " \"position\": 3,\r",
- " \"selectionOptions\": [\"postman-multiplechoice-answer-1-update\", \"postman-multiplechoice-answer-2-update\"]\r",
- " })\r",
- " }\r",
- " }, function (err, response) {\r",
- " response = response.json();\r",
+ " // Initial answer count must equal 0\r",
+ " pm.expect(response.answers[0].answerCount).to.equal(0);\r",
"\r",
- " // New selection option is removed and correct answer updated\r",
- " pm.expect(response.answers[0].selectionOption).to.equal(\"postman-multiplechoice-answer-1-update\");\r",
- " pm.expect(response.answers[1].selectionOption).to.equal(\"postman-multiplechoice-answer-2-update\");\r",
- " });\r",
+ " cb(err, response);\r",
+ " });\r",
+ " }),\r",
+ "\r",
+ " (cb) => pm.test(\"Selection option that existed in database but not anymore in update is removed\", () => {\r",
+ " pm.sendRequest({\r",
+ " url: `${baseUrl}/v1/poll-items/multiple-choice/${multipleChoiceId}`,\r",
+ " method: 'PUT',\r",
+ " header: {\r",
+ " 'Cookie': cookies,\r",
+ " 'Content-Type': 'application/json'\r",
+ " },\r",
+ " body: {\r",
+ " mode: 'raw',\r",
+ " raw: JSON.stringify({\r",
+ " \"pollId\": `${pollId}`,\r",
+ " \"question\": \"postman-multiplechoice-question-update\",\r",
+ " \"position\": 3,\r",
+ " \"selectionOptions\": [\"a\", \"b\"]\r",
+ " })\r",
+ " }\r",
+ " }, function (err, response) {\r",
+ " response = response.json();\r",
+ "\r",
+ " // New selection option is removed and correct answer updated\r",
+ " pm.expect(response.answers[0].selectionOption).to.equal(\"a\");\r",
+ " pm.expect(response.answers[1].selectionOption).to.equal(\"b\");\r",
+ "\r",
+ " cb(err, response);\r",
+ " });\r",
+ " })\r",
+ "\r",
+ "], (err, res) => {\r",
+ " console.log('Series operations resolved (Update multiple choice item)', err, res);\r",
"});\r",
""
],
@@ -1472,7 +1613,7 @@
],
"body": {
"mode": "raw",
- "raw": "{\r\n \"pollId\": \"{{poll-id}}\",\r\n \"question\": \"postman-multiplechoice-question-update\",\r\n \"position\": 3,\r\n \"selectionOptions\": [\"postman-multiplechoice-answer-1-update\", \"postman-multiplechoice-answer-2-update\"]\r\n}",
+ "raw": "{\r\n \"pollId\": \"{{poll-id}}\",\r\n \"question\": \"postman-multiplechoice-question-update\",\r\n \"position\": 3,\r\n \"selectionOptions\": [\"a\", \"b\"]\r\n}",
"options": {
"raw": {
"language": "json"
@@ -1501,6 +1642,53 @@
"listen": "test",
"script": {
"exec": [
+ "// Async operations\r",
+ "// https://community.postman.com/t/async-operations/24314/3\r",
+ "\r",
+ "/**\r",
+ " * @private\r",
+ " * @description Internal function to run tasks in series\r",
+ " * \r",
+ " * @param {Array} tasks\r",
+ " * @param {Function} cb\r",
+ " * @param {Number} currOperation\r",
+ " * @param {Array} results\r",
+ " */\r",
+ "function _series (tasks, cb, currOperation = 0, results = []) {\r",
+ " // Bail-out condition\r",
+ " if (currOperation === tasks.length) {\r",
+ " return cb(null, results);\r",
+ " }\r",
+ "\r",
+ " if (typeof tasks[currOperation] !== 'function') {\r",
+ " return cb(new Error('asyncSeries: Please provide a function'));\r",
+ " }\r",
+ "\r",
+ " tasks[currOperation]((err, res) => {\r",
+ " if (err) {\r",
+ " return cb(err);\r",
+ " }\r",
+ "\r",
+ " results.push(res);\r",
+ "\r",
+ " // Recursively call the next task in series till we're done executing all the operations\r",
+ " return _series(tasks, cb, currOperation + 1, results);\r",
+ " });\r",
+ "}\r",
+ "\r",
+ "/**\r",
+ " * @description asyncSeries to execute requests in a series format\r",
+ " * \r",
+ " * @param {Array} tasks\r",
+ " * @param {Function} cb\r",
+ " */\r",
+ "function asyncSeries (tasks, cb = () => {}) {\r",
+ " return _series(tasks, cb);\r",
+ "}\r",
+ "\r",
+ "/////////////////////////////////////////////////////////////////////////////////////////////////////////\r",
+ "\r",
+ "\r",
"const baseUrl = pm.environment.get('base-url');\r",
"const pollId = pm.globals.get('poll-id');\r",
"const cookies = pm.environment.get('cookies');\r",
@@ -1515,129 +1703,298 @@
" pm.response.to.have.status(200);\r",
"});\r",
"\r",
+ "asyncSeries([\r",
"\r",
- "// ---------------------- Position -------------------------------\r",
- "pm.test(\"Quiz item stays at position 1\", () => {\r",
- " pm.sendRequest({\r",
- " url: `${baseUrl}/v1/polls/${pollId}/poll-items`,\r",
- " method: 'GET',\r",
- " header: {\r",
- " 'Cookie': cookies\r",
- " }\r",
- " }, function (err, response) {\r",
- " response = response.json();\r",
+ " // ---------------------- Position -------------------------------\r",
+ " (cb) => pm.test(\"Quiz item stays at position 1\", () => {\r",
+ " pm.sendRequest({\r",
+ " url: `${baseUrl}/v1/polls/${pollId}/poll-items`,\r",
+ " method: 'GET',\r",
+ " header: {\r",
+ " 'Cookie': cookies\r",
+ " }\r",
+ " }, function (err, response) {\r",
+ " response = response.json();\r",
"\r",
- " // Quiz item stays at position 1\r",
- " pm.expect(response[0].itemId).to.equal(multipleChoiceId);\r",
- " pm.expect(response[0].position).to.equal(3);\r",
+ " // Quiz item stays at position 1\r",
+ " pm.expect(response[0].itemId).to.equal(multipleChoiceId);\r",
+ " pm.expect(response[0].position).to.equal(3);\r",
"\r",
- " pm.expect(response[1].itemId).to.equal(quizId);\r",
- " pm.expect(response[1].position).to.equal(1);\r",
+ " pm.expect(response[1].itemId).to.equal(quizId);\r",
+ " pm.expect(response[1].position).to.equal(1);\r",
"\r",
- " pm.expect(response[2].itemId).to.equal(openTextId);\r",
- " pm.expect(response[2].position).to.equal(2);\r",
- " });\r",
- "});\r",
+ " pm.expect(response[2].itemId).to.equal(openTextId);\r",
+ " pm.expect(response[2].position).to.equal(2);\r",
"\r",
+ " cb(err, response);\r",
+ " });\r",
+ " }),\r",
"\r",
- "// -------------------- Selection options -------------------------------\r",
- "pm.test(\"Selection options that already exist are kept\", () => {\r",
- " pm.sendRequest({\r",
- " url: `${baseUrl}/v1/poll-items/quiz/${quizId}`,\r",
- " method: 'PUT',\r",
- " header: {\r",
- " 'Cookie': cookies,\r",
- " 'Content-Type': 'application/json'\r",
- " },\r",
- " body: {\r",
- " mode: 'raw',\r",
- " raw: JSON.stringify({\r",
+ " // -------------------- Selection options -------------------------------\r",
+ " (cb) => pm.test(\"Selection options that already exist are kept\", () => {\r",
+ " pm.sendRequest({\r",
+ " url: `${baseUrl}/v1/poll-items/quiz/${quizId}`,\r",
+ " method: 'PUT',\r",
+ " header: {\r",
+ " 'Cookie': cookies,\r",
+ " 'Content-Type': 'application/json'\r",
+ " },\r",
+ " body: {\r",
+ " mode: 'raw',\r",
+ " raw: JSON.stringify({\r",
+ " \"pollId\": `${pollId}`,\r",
+ " \"question\": \"postman-quiz-question-update\",\r",
+ " \"position\": 1,\r",
+ " \"selectionOptions\": [\"a\", \"b\", \"c\"]\r",
+ " })\r",
+ " }\r",
+ " }, function (err, response) {\r",
+ " response = response.json();\r",
+ "\r",
+ " // Selection option already existed and are kept\r",
+ " pm.expect(response.answers[0].selectionOption).to.equal(\"a\");\r",
+ " pm.expect(response.answers[0].isCorrect).to.be.true;\r",
+ " pm.expect(response.answers[1].selectionOption).to.equal(\"b\");\r",
+ " pm.expect(response.answers[1].isCorrect).to.be.false;\r",
+ " pm.expect(response.answers[2].selectionOption).to.equal(\"c\");\r",
+ " pm.expect(response.answers[2].isCorrect).to.be.false;\r",
+ " // TODO: test in conjunction with websockets to make sure that answer count also stays the same (meaning: it is not reset to 0 !!)\r",
+ "\r",
+ " cb(err, response);\r",
+ " });\r",
+ " }),\r",
+ "\r",
+ " (cb) => pm.test(\"New selection options are added (with initial answer count of 0) & order is guaranteed\", () => {\r",
+ " pm.sendRequest({\r",
+ " url: `${baseUrl}/v1/poll-items/quiz/${quizId}`,\r",
+ " method: 'PUT',\r",
+ " header: {\r",
+ " 'Cookie': cookies,\r",
+ " 'Content-Type': 'application/json'\r",
+ " },\r",
+ " body: {\r",
+ " mode: 'raw',\r",
+ " raw: JSON.stringify({\r",
" \"pollId\": `${pollId}`,\r",
- " \"question\": \"postman-quiz-question-update\",\r",
- " \"position\": 1,\r",
- " \"selectionOptions\": [ \"correct option update\", \"wrong option update\", \"wrong option update2\"]\r",
- " })\r",
- " }\r",
- " }, function (err, response) {\r",
- " response = response.json();\r",
+ " \"question\": \"postman-quiz-question-update\",\r",
+ " \"position\": 1,\r",
+ " \"selectionOptions\": [ \"a\", \"b\", \"d\", \"c\"]\r",
+ " })\r",
+ " }\r",
+ " }, function (err, response) {\r",
+ " response = response.json();\r",
"\r",
- " // Selection option already existed and are kept\r",
- " pm.expect(response.answers[0].selectionOption).to.equal(\"correct option update\");\r",
- " pm.expect(response.answers[0].isCorrect).to.be.true;\r",
- " pm.expect(response.answers[1].selectionOption).to.equal(\"wrong option update\");\r",
- " pm.expect(response.answers[1].isCorrect).to.be.false;\r",
- " pm.expect(response.answers[2].selectionOption).to.equal(\"wrong option update2\");\r",
- " pm.expect(response.answers[2].isCorrect).to.be.false;\r",
- " // TODO: test in conjunction with websockets to make sure that answer count also stays the same\r",
- " });\r",
- "});\r",
+ " // New selection option is added (at the end, but no order is guaranteed!)\r",
+ " pm.expect(response.answers[0].selectionOption).to.equal(\"a\");\r",
+ " pm.expect(response.answers[0].isCorrect).to.be.true;\r",
+ " pm.expect(response.answers[1].selectionOption).to.equal(\"b\");\r",
+ " pm.expect(response.answers[1].isCorrect).to.be.false;\r",
+ " pm.expect(response.answers[2].selectionOption).to.equal(\"d\");\r",
+ " pm.expect(response.answers[2].isCorrect).to.be.false;\r",
+ " pm.expect(response.answers[3].selectionOption).to.equal(\"c\");\r",
+ " pm.expect(response.answers[3].isCorrect).to.be.false;\r",
"\r",
- "pm.test(\"New selection options are added (with initial answer count of 0)\", () => {\r",
- " pm.sendRequest({\r",
- " url: `${baseUrl}/v1/poll-items/quiz/${quizId}`,\r",
- " method: 'PUT',\r",
- " header: {\r",
- " 'Cookie': cookies,\r",
- " 'Content-Type': 'application/json'\r",
- " },\r",
- " body: {\r",
- " mode: 'raw',\r",
- " raw: JSON.stringify({\r",
- " \"pollId\": `${pollId}`,\r",
- " \"question\": \"postman-quiz-question-update\",\r",
- " \"position\": 1,\r",
- " \"selectionOptions\": [ \"correct option update\", \"wrong option update\", \"new wrong option\", \"wrong option update2\"]\r",
- " })\r",
- " }\r",
- " }, function (err, response) {\r",
- " response = response.json();\r",
+ " // Initial answer count must equal 0\r",
+ " pm.expect(response.answers[2].answerCount).to.equal(0);\r",
"\r",
- " // New selection option is added (at the end, but no order is guaranteed!)\r",
- " pm.expect(response.answers[0].selectionOption).to.equal(\"correct option update\");\r",
- " pm.expect(response.answers[0].isCorrect).to.be.true;\r",
- " pm.expect(response.answers[1].selectionOption).to.equal(\"wrong option update\");\r",
- " pm.expect(response.answers[1].isCorrect).to.be.false;\r",
- " pm.expect(response.answers[2].selectionOption).to.equal(\"wrong option update2\");\r",
- " pm.expect(response.answers[2].isCorrect).to.be.false;\r",
- " pm.expect(response.answers[3].selectionOption).to.equal(\"new wrong option\");\r",
- " pm.expect(response.answers[3].isCorrect).to.be.false;\r",
+ " cb(err, response);\r",
+ " });\r",
+ " }),\r",
"\r",
- " // Initial answer count must equal 0\r",
- " pm.expect(response.answers[3].answerCount).to.equal(0)\r",
- " });\r",
- "});\r",
+ " (cb) => pm.test(\"Selection option that existed in database but not anymore in update is removed\", () => {\r",
+ " pm.sendRequest({\r",
+ " url: `${baseUrl}/v1/poll-items/quiz/${quizId}`,\r",
+ " method: 'PUT',\r",
+ " header: {\r",
+ " 'Cookie': cookies,\r",
+ " 'Content-Type': 'application/json'\r",
+ " },\r",
+ " body: {\r",
+ " mode: 'raw',\r",
+ " raw: JSON.stringify({\r",
+ " \"pollId\": `${pollId}`,\r",
+ " \"question\": \"postman-quiz-question-update\",\r",
+ " \"position\": 1,\r",
+ " \"selectionOptions\": [\"a\", \"b\", \"c\"]\r",
+ " })\r",
+ " }\r",
+ " }, function (err, response) {\r",
+ " response = response.json();\r",
+ " \r",
+ " // \"d\" is removed\r",
+ " // TODO: should only be possible if \"d\" has answerCount == 0\r",
+ " pm.expect(response.answers.length).to.be.equal(3); // only three elements left\r",
+ " pm.expect(response.answers[0].selectionOption).to.equal(\"a\");\r",
+ " pm.expect(response.answers[0].isCorrect).to.be.true;\r",
+ " pm.expect(response.answers[1].selectionOption).to.equal(\"b\");\r",
+ " pm.expect(response.answers[1].isCorrect).to.be.false;\r",
+ " pm.expect(response.answers[2].selectionOption).to.equal(\"c\");\r",
+ " pm.expect(response.answers[2].isCorrect).to.be.false;\r",
"\r",
- "pm.test(\"Selection option that existed in database but not anymore in update is removed\", () => {\r",
- " pm.sendRequest({\r",
- " url: `${baseUrl}/v1/poll-items/quiz/${quizId}`,\r",
- " method: 'PUT',\r",
- " header: {\r",
- " 'Cookie': cookies,\r",
- " 'Content-Type': 'application/json'\r",
- " },\r",
- " body: {\r",
- " mode: 'raw',\r",
- " raw: JSON.stringify({\r",
- " \"pollId\": `${pollId}`,\r",
- " \"question\": \"postman-quiz-question-update\",\r",
- " \"position\": 1,\r",
- " \"selectionOptions\": [\"wrong option update\", \"new wrong option\", \"wrong option update2\"]\r",
- " })\r",
- " }\r",
- " }, function (err, response) {\r",
- " response = response.json();\r",
- " \r",
- " // First option is removed. As the first item should always represent the correct option\r",
- " // a new item is the correct option now (only possible since the \"correct option update\" had an answer count of 0)\r",
- " // Sorted according to their ids:\r",
- " pm.expect(response.answers.length).to.be.equal(3); // only three elements left\r",
- " pm.expect(response.answers[0].selectionOption).to.equal(\"wrong option update\");\r",
- " pm.expect(response.answers[0].isCorrect).to.be.true;\r",
- " pm.expect(response.answers[1].selectionOption).to.equal(\"wrong option update2\");\r",
- " pm.expect(response.answers[1].isCorrect).to.be.false;\r",
- " pm.expect(response.answers[2].selectionOption).to.equal(\"new wrong option\");\r",
- " pm.expect(response.answers[2].isCorrect).to.be.false;\r",
- " });\r",
+ " cb(err, response);\r",
+ " });\r",
+ " }),\r",
+ "\r",
+ " (cb) => pm.test(\"Remove first element, thus the next element is the correct one\", () => {\r",
+ " pm.sendRequest({\r",
+ " url: `${baseUrl}/v1/poll-items/quiz/${quizId}`,\r",
+ " method: 'PUT',\r",
+ " header: {\r",
+ " 'Cookie': cookies,\r",
+ " 'Content-Type': 'application/json'\r",
+ " },\r",
+ " body: {\r",
+ " mode: 'raw',\r",
+ " raw: JSON.stringify({\r",
+ " \"pollId\": `${pollId}`,\r",
+ " \"question\": \"postman-quiz-question-update\",\r",
+ " \"position\": 1,\r",
+ " \"selectionOptions\": [\"b\", \"c\"]\r",
+ " })\r",
+ " }\r",
+ " }, function (err, response) {\r",
+ " response = response.json();\r",
+ " \r",
+ " // First option is removed. As the first item should always represent the correct option\r",
+ " // \"b\" is the correct option now (since b is the first string in the updated list)\r",
+ " // TODO: should only be possible if \"a\" has answerCount == 0\r",
+ " pm.expect(response.answers.length).to.be.equal(2); // only three elements left\r",
+ " pm.expect(response.answers[0].selectionOption).to.equal(\"b\");\r",
+ " pm.expect(response.answers[0].isCorrect).to.be.true;\r",
+ " pm.expect(response.answers[1].selectionOption).to.equal(\"c\");\r",
+ " pm.expect(response.answers[1].isCorrect).to.be.false;\r",
+ "\r",
+ " cb(err, response);\r",
+ " });\r",
+ " }),\r",
+ "\r",
+ " (cb) => pm.test(\"Insert new correct selection option at the beginning\", () => {\r",
+ " pm.sendRequest({\r",
+ " url: `${baseUrl}/v1/poll-items/quiz/${quizId}`,\r",
+ " method: 'PUT',\r",
+ " header: {\r",
+ " 'Cookie': cookies,\r",
+ " 'Content-Type': 'application/json'\r",
+ " },\r",
+ " body: {\r",
+ " mode: 'raw',\r",
+ " raw: JSON.stringify({\r",
+ " \"pollId\": `${pollId}`,\r",
+ " \"question\": \"postman-quiz-question-update\",\r",
+ " \"position\": 1,\r",
+ " \"selectionOptions\": [\"a\", \"b\", \"c\"]\r",
+ " })\r",
+ " }\r",
+ " }, function (err, response) {\r",
+ " response = response.json();\r",
+ " \r",
+ " // \"a\" is now the correct selection option\r",
+ " // TODO: should only be possible if \"b\" has answerCount == 0\r",
+ " pm.expect(response.answers.length).to.be.equal(3); // only three elements left\r",
+ " pm.expect(response.answers[0].selectionOption).to.equal(\"a\");\r",
+ " pm.expect(response.answers[0].isCorrect).to.be.true;\r",
+ " pm.expect(response.answers[1].selectionOption).to.equal(\"b\");\r",
+ " pm.expect(response.answers[1].isCorrect).to.be.false;\r",
+ " pm.expect(response.answers[2].selectionOption).to.equal(\"c\");\r",
+ " pm.expect(response.answers[2].isCorrect).to.be.false;\r",
+ "\r",
+ " cb(err, response);\r",
+ " });\r",
+ " }),\r",
+ "\r",
+ " (cb) => pm.test(\"Choose a new correct answer & change order (1)\", () => {\r",
+ " pm.sendRequest({\r",
+ " url: `${baseUrl}/v1/poll-items/quiz/${quizId}`,\r",
+ " method: 'PUT',\r",
+ " header: {\r",
+ " 'Cookie': cookies,\r",
+ " 'Content-Type': 'application/json'\r",
+ " },\r",
+ " body: {\r",
+ " mode: 'raw',\r",
+ " raw: JSON.stringify({\r",
+ " \"pollId\": `${pollId}`,\r",
+ " \"question\": \"postman-quiz-question-update\",\r",
+ " \"position\": 1,\r",
+ " \"selectionOptions\": [\"b\", \"c\", \"a\"]\r",
+ " })\r",
+ " }\r",
+ " }, function (err, response) {\r",
+ " response = response.json();\r",
+ " \r",
+ " // \"b\" is the new correct option\r",
+ " // TODO: should only be possible if \"a\" has answerCount == 0 (a was the correct option before)\r",
+ " pm.expect(response.answers.length).to.be.equal(3); // only three elements left\r",
+ " pm.expect(response.answers[0].selectionOption).to.equal(\"b\");\r",
+ " pm.expect(response.answers[0].isCorrect).to.be.true;\r",
+ " pm.expect(response.answers[1].selectionOption).to.equal(\"c\");\r",
+ " pm.expect(response.answers[1].isCorrect).to.be.false;\r",
+ " pm.expect(response.answers[2].selectionOption).to.equal(\"a\");\r",
+ " pm.expect(response.answers[2].isCorrect).to.be.false;\r",
+ "\r",
+ " cb(err, response);\r",
+ " });\r",
+ " }),\r",
+ "\r",
+ " (cb) => pm.test(\"Choose a new correct answer & change order (2)\", () => {\r",
+ " pm.sendRequest({\r",
+ " url: `${baseUrl}/v1/poll-items/quiz/${quizId}`,\r",
+ " method: 'PUT',\r",
+ " header: {\r",
+ " 'Cookie': cookies,\r",
+ " 'Content-Type': 'application/json'\r",
+ " },\r",
+ " body: {\r",
+ " mode: 'raw',\r",
+ " raw: JSON.stringify({\r",
+ " \"pollId\": `${pollId}`,\r",
+ " \"question\": \"postman-quiz-question-update\",\r",
+ " \"position\": 1,\r",
+ " \"selectionOptions\": [\"c\", \"b\", \"a\"]\r",
+ " })\r",
+ " }\r",
+ " }, function (err, response) {\r",
+ " response = response.json();\r",
+ " \r",
+ " // \"c\" is the new correct option\r",
+ " // TODO: should only be possible if \"b\" has answerCount == 0 (b was the correct option before)\r",
+ " pm.expect(response.answers.length).to.be.equal(3); // only three elements left\r",
+ " pm.expect(response.answers[0].selectionOption).to.equal(\"c\");\r",
+ " pm.expect(response.answers[0].isCorrect).to.be.true;\r",
+ " pm.expect(response.answers[1].selectionOption).to.equal(\"b\");\r",
+ " pm.expect(response.answers[1].isCorrect).to.be.false;\r",
+ " pm.expect(response.answers[2].selectionOption).to.equal(\"a\");\r",
+ " pm.expect(response.answers[2].isCorrect).to.be.false;\r",
+ "\r",
+ " cb(err, response);\r",
+ " });\r",
+ " }),\r",
+ "\r",
+ " (cb) => pm.test(\"Order is preserved even for the GET poll item endpoint (1)\", () => {\r",
+ " pm.sendRequest({\r",
+ " url: `${baseUrl}/v1/poll-items/${quizId}`,\r",
+ " method: 'GET',\r",
+ " header: {\r",
+ " 'Cookie': cookies,\r",
+ " 'Content-Type': 'application/json'\r",
+ " }\r",
+ " }, function (err, response) {\r",
+ " response = response.json();\r",
+ " \r",
+ " pm.expect(response.answers.length).to.be.equal(3); // only three elements left\r",
+ " pm.expect(response.answers[0].selectionOption).to.equal(\"c\");\r",
+ " pm.expect(response.answers[0].isCorrect).to.be.true;\r",
+ " pm.expect(response.answers[1].selectionOption).to.equal(\"b\");\r",
+ " pm.expect(response.answers[1].isCorrect).to.be.false;\r",
+ " pm.expect(response.answers[2].selectionOption).to.equal(\"a\");\r",
+ " pm.expect(response.answers[2].isCorrect).to.be.false;\r",
+ "\r",
+ " cb(err, response);\r",
+ " });\r",
+ " }),\r",
+ "\r",
+ "], (err, res) => {\r",
+ " console.log('Series operations resolved (Update quiz item)', err, res);\r",
"});\r",
"\r",
"// TODO: have an item with answer count > 0 and test\r",
@@ -1670,7 +2027,7 @@
],
"body": {
"mode": "raw",
- "raw": "{\r\n \"pollId\": \"{{poll-id}}\",\r\n \"question\": \"postman-quiz-question-update\",\r\n \"position\": 1,\r\n \"selectionOptions\": [ \"correct option update\", \"wrong option update\", \"wrong option update2\"]\r\n}",
+ "raw": "{\r\n \"pollId\": \"{{poll-id}}\",\r\n \"question\": \"postman-quiz-question-update\",\r\n \"position\": 1,\r\n \"selectionOptions\": [ \"a\", \"b\", \"c\"]\r\n}",
"options": {
"raw": {
"language": "json"
@@ -1842,8 +2199,8 @@
" headers: 'Cookie:'+pm.environment.get(\"cookies\")\r",
" }, function(error, response) {\r",
" pm.test(\"Poll item does not exist anymore\", function() {\r",
- " pm.expect(response).to.have.property('code', 403)\r",
- " pm.expect(response).to.have.property('status', 'Forbidden')\r",
+ " pm.expect(response).to.have.property('code', 404)\r",
+ " pm.expect(response).to.have.property('status', 'Not Found')\r",
" })\r",
"})\r",
"\r",
diff --git a/src/main/kotlin/de/livepoll/api/LivePollApplication.kt b/src/main/kotlin/de/livepoll/api/LivePollApplication.kt
index 4d1ecef0..f1aebfbd 100644
--- a/src/main/kotlin/de/livepoll/api/LivePollApplication.kt
+++ b/src/main/kotlin/de/livepoll/api/LivePollApplication.kt
@@ -1,7 +1,5 @@
package de.livepoll.api
-import de.livepoll.api.entity.db.User
-import de.livepoll.api.repository.UserRepository
import de.livepoll.api.service.AccountService
import org.springframework.boot.CommandLineRunner
import org.springframework.boot.autoconfigure.SpringBootApplication
@@ -9,14 +7,14 @@ import org.springframework.boot.runApplication
@SpringBootApplication(scanBasePackages = ["de.livepoll.api"])
class LivePollApplication(
- private val accountService: AccountService,
-): CommandLineRunner {
- override fun run(vararg args: String?) {
- // Create postman user, when started in testing environment
- if (System.getenv("LIVE_POLL_POSTMAN") == "true") accountService.createPostmanAccount()
- }
+ private val accountService: AccountService,
+) : CommandLineRunner {
+ override fun run(vararg args: String?) {
+ // Create postman user, when started in testing environment
+ if (System.getenv("LIVE_POLL_POSTMAN") == "true") accountService.createPostmanAccount()
+ }
}
fun main(args: Array) {
- runApplication(*args)
-}
\ No newline at end of file
+ runApplication(*args)
+}
diff --git a/src/main/kotlin/de/livepoll/api/config/BlockedTokenConfig.kt b/src/main/kotlin/de/livepoll/api/config/BlockedTokenConfig.kt
index 616dc498..42610e1c 100644
--- a/src/main/kotlin/de/livepoll/api/config/BlockedTokenConfig.kt
+++ b/src/main/kotlin/de/livepoll/api/config/BlockedTokenConfig.kt
@@ -9,7 +9,7 @@ import java.util.*
@Configuration
@EnableScheduling
class BlockedTokenConfig(
- val blockedTokenRepository: BlockedTokenRepository
+ val blockedTokenRepository: BlockedTokenRepository
) {
@Scheduled(cron = "0 0 12 * * ?")
@@ -21,4 +21,4 @@ class BlockedTokenConfig(
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/main/kotlin/de/livepoll/api/config/CorsConfig.kt b/src/main/kotlin/de/livepoll/api/config/CorsConfig.kt
index ec9b261c..e7f277a1 100644
--- a/src/main/kotlin/de/livepoll/api/config/CorsConfig.kt
+++ b/src/main/kotlin/de/livepoll/api/config/CorsConfig.kt
@@ -12,18 +12,18 @@ import org.springframework.web.servlet.view.InternalResourceViewResolver
class CorsConfig : WebMvcConfigurer {
private val allowedOrigins = setOf(
- System.getenv("LIVE_POLL_DEV_URL"),
- "https://dev.live-poll.de",
- "https://www.live-poll.de"
+ System.getenv("LIVE_POLL_FRONTEND_URL"),
+ System.getenv("LIVE_POLL_DEV_URL")
)
override fun addCorsMappings(registry: CorsRegistry) {
registry.addMapping("/**")
- .allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE")
- .allowedOrigins(*allowedOrigins.toTypedArray())
- .allowCredentials(true)
+ .allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE")
+ .allowedOrigins(*allowedOrigins.toTypedArray())
+ .allowCredentials(true)
}
@Bean
fun defaultViewResolver() = InternalResourceViewResolver()
-}
\ No newline at end of file
+
+}
diff --git a/src/main/kotlin/de/livepoll/api/config/QuartzSchedulerConfig.kt b/src/main/kotlin/de/livepoll/api/config/QuartzSchedulerConfig.kt
index 2430c63e..4971c3b1 100644
--- a/src/main/kotlin/de/livepoll/api/config/QuartzSchedulerConfig.kt
+++ b/src/main/kotlin/de/livepoll/api/config/QuartzSchedulerConfig.kt
@@ -6,7 +6,8 @@ import org.springframework.context.ApplicationContext
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.io.ClassPathResource
-import org.springframework.scheduling.quartz.*
+import org.springframework.scheduling.quartz.SchedulerFactoryBean
+import org.springframework.scheduling.quartz.SpringBeanJobFactory
import javax.sql.DataSource
@Configuration
@@ -34,4 +35,4 @@ class QuartzSchedulerConfig {
return schedulerFactory
}
-}
\ No newline at end of file
+}
diff --git a/src/main/kotlin/de/livepoll/api/config/SecurityConfig.kt b/src/main/kotlin/de/livepoll/api/config/SecurityConfig.kt
index 53d39aa6..de2c5d38 100644
--- a/src/main/kotlin/de/livepoll/api/config/SecurityConfig.kt
+++ b/src/main/kotlin/de/livepoll/api/config/SecurityConfig.kt
@@ -4,6 +4,7 @@ import de.livepoll.api.service.JwtUserDetailsService
import de.livepoll.api.util.JwtRequestFilter
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
+import org.springframework.security.authentication.AuthenticationManager
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.builders.WebSecurity
@@ -12,7 +13,6 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
import org.springframework.security.config.http.SessionCreationPolicy
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
-import org.springframework.security.authentication.AuthenticationManager
@Configuration
@EnableWebSecurity
@@ -24,14 +24,15 @@ class SecurityConfig(
override fun configure(http: HttpSecurity) {
http.cors().and().csrf().disable()
.authorizeRequests()
- .antMatchers("/v1/account/register").permitAll()
- .antMatchers("/v1/account/confirm").permitAll()
- .antMatchers("/v1/account/login").permitAll()
- .antMatchers("/v1/websocket/**").permitAll()
- .antMatchers("/actuator/**").permitAll()
- //.antMatchers("/admin").hasRole("ADMIN") // TODO: introduce ROLE_ADMIN authority later on
- .anyRequest().authenticated()
- .and()
+ .antMatchers("/v1/account/register").permitAll()
+ .antMatchers("/v1/account/confirm").permitAll()
+ .antMatchers("/v1/account/login").permitAll()
+ .antMatchers("/v1/polls/{id:[\\d]+}").permitAll()
+ .antMatchers("/v1/websocket/**").permitAll()
+ .antMatchers("/actuator/**").permitAll()
+ //.antMatchers("/admin").hasRole("ADMIN") // TODO: introduce ROLE_ADMIN authority later on
+ .anyRequest().authenticated()
+ .and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter::class.java)
diff --git a/src/main/kotlin/de/livepoll/api/config/SwaggerConfig.kt b/src/main/kotlin/de/livepoll/api/config/SwaggerConfig.kt
index 975c46a0..2447d778 100644
--- a/src/main/kotlin/de/livepoll/api/config/SwaggerConfig.kt
+++ b/src/main/kotlin/de/livepoll/api/config/SwaggerConfig.kt
@@ -17,28 +17,28 @@ class SwaggerConfig {
@Bean
fun api(): Docket = Docket(DocumentationType.OAS_30)
- .ignoredParameterTypes(AuthenticationPrincipal::class.java)
- .host("api.live-poll.de")
- .enableUrlTemplating(true)
- .select()
- .apis(RequestHandlerSelectors.basePackage("de.livepoll.api"))
- .paths(PathSelectors.any())
- .build()
- .apiInfo(apiInfo())
+ .ignoredParameterTypes(AuthenticationPrincipal::class.java)
+ .host("api.live-poll.de")
+ .enableUrlTemplating(true)
+ .select()
+ .apis(RequestHandlerSelectors.basePackage("de.livepoll.api"))
+ .paths(PathSelectors.any())
+ .build()
+ .apiInfo(apiInfo())
@Bean
fun uiConfig(): UiConfiguration = UiConfigurationBuilder.builder()
- .defaultModelsExpandDepth(-1)
- .build()
+ .defaultModelsExpandDepth(-1)
+ .build()
private fun apiInfo() = ApiInfo(
- "Live-Poll API",
- "Platform for providing surveys with live display features",
- "1.0.1",
- "https://chillibits.com/pmapp?p=privacy",
- Contact("ChilliBits", "https://www.chillibits.com", "contact@chillibits.com"),
- "ODC DbCL v1.0 License",
- "https://opendatacommons.org/licenses/dbcl/1.0/",
- emptyList()
+ "Live-Poll API",
+ "Platform for providing surveys with live display features",
+ "1.0.1",
+ "https://chillibits.com/pmapp?p=privacy",
+ Contact("ChilliBits", "https://www.chillibits.com", "contact@chillibits.com"),
+ "ODC DbCL v1.0 License",
+ "https://opendatacommons.org/licenses/dbcl/1.0/",
+ emptyList()
)
-}
\ No newline at end of file
+}
diff --git a/src/main/kotlin/de/livepoll/api/config/WebSocketConfig.kt b/src/main/kotlin/de/livepoll/api/config/WebSocketConfig.kt
index cc51e090..7cdb3bf4 100644
--- a/src/main/kotlin/de/livepoll/api/config/WebSocketConfig.kt
+++ b/src/main/kotlin/de/livepoll/api/config/WebSocketConfig.kt
@@ -12,7 +12,7 @@ import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerCo
@Configuration
@EnableWebSocketMessageBroker
class WebSocketConfig(
- private val corsConfig: CorsConfig
+ private val corsConfig: CorsConfig
) : WebSocketMessageBrokerConfigurer {
override fun configureMessageBroker(registry: MessageBrokerRegistry) {
@@ -22,9 +22,9 @@ class WebSocketConfig(
override fun registerStompEndpoints(registry: StompEndpointRegistry) {
registry.addEndpoint("/v1/websocket/enter-poll")
- .addInterceptors(HttpHandshakeInterceptor())
- .setHandshakeHandler(CustomWebSocketHandshakeHandler())
- .setAllowedOrigins("*")
+ .addInterceptors(HttpHandshakeInterceptor())
+ .setHandshakeHandler(CustomWebSocketHandshakeHandler())
+ .setAllowedOrigins("*")
}
-}
\ No newline at end of file
+}
diff --git a/src/main/kotlin/de/livepoll/api/controller/PollController.kt b/src/main/kotlin/de/livepoll/api/controller/PollController.kt
index 64e16bfb..6491e579 100644
--- a/src/main/kotlin/de/livepoll/api/controller/PollController.kt
+++ b/src/main/kotlin/de/livepoll/api/controller/PollController.kt
@@ -36,8 +36,7 @@ class PollController(
@ApiOperation(value = "Get poll", tags = ["Poll"])
@GetMapping("/{id}")
- fun getPoll(@PathVariable(name = "id") pollId: Long, @AuthenticationPrincipal user: User): PollDtoOut {
- accountService.checkAuthorizationByPollId(pollId)
+ fun getPoll(@PathVariable(name = "id") pollId: Long): PollDtoOut {
return pollService.getPoll(pollId)
}
diff --git a/src/main/kotlin/de/livepoll/api/controller/WebSocketController.kt b/src/main/kotlin/de/livepoll/api/controller/WebSocketController.kt
index 6e25414e..7d28c16c 100644
--- a/src/main/kotlin/de/livepoll/api/controller/WebSocketController.kt
+++ b/src/main/kotlin/de/livepoll/api/controller/WebSocketController.kt
@@ -8,7 +8,7 @@ import org.springframework.stereotype.Controller
@Controller
class WebSocketController(
- private val webSocketService: WebSocketService
+ private val webSocketService: WebSocketService
) {
@MessageMapping("/{pollItemId}")
diff --git a/src/main/kotlin/de/livepoll/api/entity/db/BlockedToken.kt b/src/main/kotlin/de/livepoll/api/entity/db/BlockedToken.kt
index f819286b..8b4afa31 100644
--- a/src/main/kotlin/de/livepoll/api/entity/db/BlockedToken.kt
+++ b/src/main/kotlin/de/livepoll/api/entity/db/BlockedToken.kt
@@ -6,14 +6,14 @@ import javax.persistence.*
@Entity
@Table(name = "blocked_token")
data class BlockedToken(
- @Id
- @Column(nullable = false)
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- val id: Long,
+ @Id
+ @Column(nullable = false)
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ val id: Long,
- @Column(nullable = false)
- val token: String,
+ @Column(nullable = false)
+ val token: String,
- @Column(nullable = false)
- val expiryDate: Date
-)
\ No newline at end of file
+ @Column(nullable = false)
+ val expiryDate: Date
+)
diff --git a/src/main/kotlin/de/livepoll/api/entity/db/MultipleChoiceItemAnswer.kt b/src/main/kotlin/de/livepoll/api/entity/db/MultipleChoiceItemAnswer.kt
index 1280cf62..e2c447f4 100644
--- a/src/main/kotlin/de/livepoll/api/entity/db/MultipleChoiceItemAnswer.kt
+++ b/src/main/kotlin/de/livepoll/api/entity/db/MultipleChoiceItemAnswer.kt
@@ -6,7 +6,7 @@ import javax.persistence.*
@Entity
@Table(name = "multiple_choice_item_answer")
-class MultipleChoiceItemAnswer (
+class MultipleChoiceItemAnswer(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
diff --git a/src/main/kotlin/de/livepoll/api/entity/db/Poll.kt b/src/main/kotlin/de/livepoll/api/entity/db/Poll.kt
index 76814f4f..ccc7d5c2 100644
--- a/src/main/kotlin/de/livepoll/api/entity/db/Poll.kt
+++ b/src/main/kotlin/de/livepoll/api/entity/db/Poll.kt
@@ -7,31 +7,31 @@ import javax.persistence.*
@Entity
@Table(name = "poll")
data class Poll(
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- @Column(name = "poll_id", nullable = false)
- var id: Long,
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ @Column(name = "poll_id", nullable = false)
+ var id: Long,
- @ManyToOne
- @JsonIgnore
- @JoinColumn(name = "user_id")
- var user: User,
+ @ManyToOne
+ @JsonIgnore
+ @JoinColumn(name = "user_id")
+ var user: User,
- @Column(nullable = false)
- var name: String,
+ @Column(nullable = false)
+ var name: String,
- @Column(nullable = true)
- var startDate: Date?,
+ @Column(nullable = true)
+ var startDate: Date?,
- @Column(nullable = true)
- var endDate: Date?,
+ @Column(nullable = true)
+ var endDate: Date?,
- var slug: String,
+ var slug: String,
- @Column(nullable = true)
- var currentItem : Long?,
+ @Column(nullable = true)
+ var currentItem: Long?,
- @JsonIgnore
- @OneToMany(mappedBy = "poll", cascade = [CascadeType.ALL], orphanRemoval = true)
- var pollItems: MutableList
+ @JsonIgnore
+ @OneToMany(mappedBy = "poll", cascade = [CascadeType.ALL], orphanRemoval = true)
+ var pollItems: MutableList
)
diff --git a/src/main/kotlin/de/livepoll/api/entity/db/UserAttr.kt b/src/main/kotlin/de/livepoll/api/entity/db/UserAttr.kt
index abce2911..3073ba4c 100644
--- a/src/main/kotlin/de/livepoll/api/entity/db/UserAttr.kt
+++ b/src/main/kotlin/de/livepoll/api/entity/db/UserAttr.kt
@@ -5,18 +5,18 @@ import javax.persistence.*
@Entity
@Table(name = "user_attr")
data class UserAttr(
- @Id
- @GeneratedValue(strategy= GenerationType.IDENTITY)
- @Column(name="user_attr_id", nullable = false)
- var id: Long,
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ @Column(name = "user_attr_id", nullable = false)
+ var id: Long,
- @OneToOne
- @JoinColumn(name="user_id")
- var user: User,
+ @OneToOne
+ @JoinColumn(name = "user_id")
+ var user: User,
- @Column(nullable = false)
- var key1: String,
+ @Column(nullable = false)
+ var key1: String,
- @Column(nullable = false)
- var value: String
-)
\ No newline at end of file
+ @Column(nullable = false)
+ var value: String
+)
diff --git a/src/main/kotlin/de/livepoll/api/entity/db/VerificationToken.kt b/src/main/kotlin/de/livepoll/api/entity/db/VerificationToken.kt
index 9c3d86f8..869f11f9 100644
--- a/src/main/kotlin/de/livepoll/api/entity/db/VerificationToken.kt
+++ b/src/main/kotlin/de/livepoll/api/entity/db/VerificationToken.kt
@@ -6,17 +6,17 @@ import javax.persistence.*
@Entity
@Table(name = "verification_token")
data class VerificationToken(
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- @Column(name="verification_token_id", nullable = false)
- val id: Long,
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ @Column(name = "verification_token_id", nullable = false)
+ val id: Long,
- @Column(name="token", nullable = false)
- val token: String,
+ @Column(name = "token", nullable = false)
+ val token: String,
- @Column(name="username", nullable = false)
- val username: String,
+ @Column(name = "username", nullable = false)
+ val username: String,
- @Column(name="expiry_date", nullable = false)
- var expiryDate: Date
-)
\ No newline at end of file
+ @Column(name = "expiry_date", nullable = false)
+ var expiryDate: Date
+)
diff --git a/src/main/kotlin/de/livepoll/api/entity/dto/MultipleChoiceItemDtoOut.kt b/src/main/kotlin/de/livepoll/api/entity/dto/MultipleChoiceItemDtoOut.kt
index e2b7653b..065d6fb9 100644
--- a/src/main/kotlin/de/livepoll/api/entity/dto/MultipleChoiceItemDtoOut.kt
+++ b/src/main/kotlin/de/livepoll/api/entity/dto/MultipleChoiceItemDtoOut.kt
@@ -1,10 +1,10 @@
package de.livepoll.api.entity.dto
class MultipleChoiceItemDtoOut(
- itemId: Long,
- pollId: Long,
- question: String,
- position: Int,
- type: String,
- val answers: List
-): PollItemDtoOut(itemId, pollId, question, position, type)
+ itemId: Long,
+ pollId: Long,
+ question: String,
+ position: Int,
+ type: String,
+ val answers: List
+) : PollItemDtoOut(itemId, pollId, question, position, type)
diff --git a/src/main/kotlin/de/livepoll/api/entity/dto/MultipleChoiceItemParticipantAnswerDtoIn.kt b/src/main/kotlin/de/livepoll/api/entity/dto/MultipleChoiceItemParticipantAnswerDtoIn.kt
index 46290eb3..31a5801f 100644
--- a/src/main/kotlin/de/livepoll/api/entity/dto/MultipleChoiceItemParticipantAnswerDtoIn.kt
+++ b/src/main/kotlin/de/livepoll/api/entity/dto/MultipleChoiceItemParticipantAnswerDtoIn.kt
@@ -3,10 +3,10 @@ package de.livepoll.api.entity.dto
import com.fasterxml.jackson.annotation.JsonProperty
data class MultipleChoiceItemParticipantAnswerDtoIn(
- @JsonProperty("id")
- val id: Long,
- @JsonProperty("type")
- val type: String,
- @JsonProperty("selectionOption")
- val selectionOption: String
-)
\ No newline at end of file
+ @JsonProperty("id")
+ val id: Long,
+ @JsonProperty("type")
+ val type: String,
+ @JsonProperty("selectionOption")
+ val selectionOption: String
+)
diff --git a/src/main/kotlin/de/livepoll/api/entity/dto/OpenTextItemDtoOut.kt b/src/main/kotlin/de/livepoll/api/entity/dto/OpenTextItemDtoOut.kt
index ddb7ab22..6ea55cc5 100644
--- a/src/main/kotlin/de/livepoll/api/entity/dto/OpenTextItemDtoOut.kt
+++ b/src/main/kotlin/de/livepoll/api/entity/dto/OpenTextItemDtoOut.kt
@@ -1,10 +1,10 @@
package de.livepoll.api.entity.dto
class OpenTextItemDtoOut(
- itemId: Long,
- pollId: Long,
- question: String,
- position: Int,
- type: String,
- val answers: List
-): PollItemDtoOut(itemId, pollId, question, position, type)
+ itemId: Long,
+ pollId: Long,
+ question: String,
+ position: Int,
+ type: String,
+ val answers: List
+) : PollItemDtoOut(itemId, pollId, question, position, type)
diff --git a/src/main/kotlin/de/livepoll/api/entity/dto/OpenTextItemParticipantAnswerDtoIn.kt b/src/main/kotlin/de/livepoll/api/entity/dto/OpenTextItemParticipantAnswerDtoIn.kt
index 6f1b6744..bf6ae31f 100644
--- a/src/main/kotlin/de/livepoll/api/entity/dto/OpenTextItemParticipantAnswerDtoIn.kt
+++ b/src/main/kotlin/de/livepoll/api/entity/dto/OpenTextItemParticipantAnswerDtoIn.kt
@@ -3,10 +3,10 @@ package de.livepoll.api.entity.dto
import com.fasterxml.jackson.annotation.JsonProperty
data class OpenTextItemParticipantAnswerDtoIn(
- @JsonProperty("id")
- val id: Long,
- @JsonProperty("type")
- val type: String,
- @JsonProperty("answer")
- val answer: String
-)
\ No newline at end of file
+ @JsonProperty("id")
+ val id: Long,
+ @JsonProperty("type")
+ val type: String,
+ @JsonProperty("answer")
+ val answer: String
+)
diff --git a/src/main/kotlin/de/livepoll/api/entity/dto/PollDtoIn.kt b/src/main/kotlin/de/livepoll/api/entity/dto/PollDtoIn.kt
index 2814a0f3..61587e6e 100644
--- a/src/main/kotlin/de/livepoll/api/entity/dto/PollDtoIn.kt
+++ b/src/main/kotlin/de/livepoll/api/entity/dto/PollDtoIn.kt
@@ -3,10 +3,10 @@ package de.livepoll.api.entity.dto
import java.util.*
data class PollDtoIn(
- val name: String,
- val startDate: Date?,
- val endDate: Date?,
- val slug: String?,
- val currentItem: Long?
+ val name: String,
+ val startDate: Date?,
+ val endDate: Date?,
+ val slug: String?,
+ val currentItem: Long?
)
diff --git a/src/main/kotlin/de/livepoll/api/entity/dto/PollDtoOut.kt b/src/main/kotlin/de/livepoll/api/entity/dto/PollDtoOut.kt
index 785b20ea..dab44014 100644
--- a/src/main/kotlin/de/livepoll/api/entity/dto/PollDtoOut.kt
+++ b/src/main/kotlin/de/livepoll/api/entity/dto/PollDtoOut.kt
@@ -3,10 +3,10 @@ package de.livepoll.api.entity.dto
import java.util.*
data class PollDtoOut(
- val id: Long,
- val name: String,
- val startDate: Date?,
- val endDate: Date?,
- val slug: String,
- var currentItem: Long?
+ val id: Long,
+ val name: String,
+ val startDate: Date?,
+ val endDate: Date?,
+ val slug: String,
+ var currentItem: Long?
)
diff --git a/src/main/kotlin/de/livepoll/api/entity/dto/PollItemDtoOut.kt b/src/main/kotlin/de/livepoll/api/entity/dto/PollItemDtoOut.kt
index 97f1618d..8eaf7909 100644
--- a/src/main/kotlin/de/livepoll/api/entity/dto/PollItemDtoOut.kt
+++ b/src/main/kotlin/de/livepoll/api/entity/dto/PollItemDtoOut.kt
@@ -1,9 +1,9 @@
package de.livepoll.api.entity.dto
open class PollItemDtoOut(
- val itemId: Long,
- val pollId: Long,
- val question: String,
- val position: Int,
- val type: String
-)
\ No newline at end of file
+ val itemId: Long,
+ val pollId: Long,
+ val question: String,
+ val position: Int,
+ val type: String
+)
diff --git a/src/main/kotlin/de/livepoll/api/entity/dto/QuizItemParticipantAnswerDtoIn.kt b/src/main/kotlin/de/livepoll/api/entity/dto/QuizItemParticipantAnswerDtoIn.kt
index a86d6477..71a206c4 100644
--- a/src/main/kotlin/de/livepoll/api/entity/dto/QuizItemParticipantAnswerDtoIn.kt
+++ b/src/main/kotlin/de/livepoll/api/entity/dto/QuizItemParticipantAnswerDtoIn.kt
@@ -3,10 +3,10 @@ package de.livepoll.api.entity.dto
import com.fasterxml.jackson.annotation.JsonProperty
data class QuizItemParticipantAnswerDtoIn(
- @JsonProperty("id")
- val id: Long,
- @JsonProperty("type")
- val type: String,
- @JsonProperty("selectionOption")
- val selectionOption: String
-)
\ No newline at end of file
+ @JsonProperty("id")
+ val id: Long,
+ @JsonProperty("type")
+ val type: String,
+ @JsonProperty("selectionOption")
+ val selectionOption: String
+)
diff --git a/src/main/kotlin/de/livepoll/api/entity/dto/UserDtoIn.kt b/src/main/kotlin/de/livepoll/api/entity/dto/UserDtoIn.kt
index 1a9e1329..b723e4f9 100644
--- a/src/main/kotlin/de/livepoll/api/entity/dto/UserDtoIn.kt
+++ b/src/main/kotlin/de/livepoll/api/entity/dto/UserDtoIn.kt
@@ -1,7 +1,7 @@
package de.livepoll.api.entity.dto
data class UserDtoIn(
- var username: String,
- var email: String,
- var password: String,
+ var username: String,
+ var email: String,
+ var password: String,
)
diff --git a/src/main/kotlin/de/livepoll/api/entity/dto/UserDtoOut.kt b/src/main/kotlin/de/livepoll/api/entity/dto/UserDtoOut.kt
index ceeb8635..997501c0 100644
--- a/src/main/kotlin/de/livepoll/api/entity/dto/UserDtoOut.kt
+++ b/src/main/kotlin/de/livepoll/api/entity/dto/UserDtoOut.kt
@@ -1,7 +1,7 @@
package de.livepoll.api.entity.dto
data class UserDtoOut(
- var id: Long,
- var username: String,
- var email: String
+ var id: Long,
+ var username: String,
+ var email: String
)
diff --git a/src/main/kotlin/de/livepoll/api/entity/jwt/AuthenticationRequest.kt b/src/main/kotlin/de/livepoll/api/entity/jwt/AuthenticationRequest.kt
index 596d030a..648c1af5 100644
--- a/src/main/kotlin/de/livepoll/api/entity/jwt/AuthenticationRequest.kt
+++ b/src/main/kotlin/de/livepoll/api/entity/jwt/AuthenticationRequest.kt
@@ -1,6 +1,6 @@
package de.livepoll.api.entity.jwt
data class AuthenticationRequest(
- var username: String,
- var password: String
-)
\ No newline at end of file
+ var username: String,
+ var password: String
+)
diff --git a/src/main/kotlin/de/livepoll/api/entity/jwt/AuthenticationResponse.kt b/src/main/kotlin/de/livepoll/api/entity/jwt/AuthenticationResponse.kt
index 713ec360..a578222e 100644
--- a/src/main/kotlin/de/livepoll/api/entity/jwt/AuthenticationResponse.kt
+++ b/src/main/kotlin/de/livepoll/api/entity/jwt/AuthenticationResponse.kt
@@ -1,5 +1,5 @@
package de.livepoll.api.entity.jwt
data class AuthenticationResponse(
- var jwt: String
-)
\ No newline at end of file
+ var jwt: String
+)
diff --git a/src/main/kotlin/de/livepoll/api/exception/EmailNotConfirmedException.kt b/src/main/kotlin/de/livepoll/api/exception/EmailNotConfirmedException.kt
index 4f140590..dd6e9d4a 100644
--- a/src/main/kotlin/de/livepoll/api/exception/EmailNotConfirmedException.kt
+++ b/src/main/kotlin/de/livepoll/api/exception/EmailNotConfirmedException.kt
@@ -1,5 +1,5 @@
package de.livepoll.api.exception
class EmailNotConfirmedException(
- override val message: String
-) : Exception()
\ No newline at end of file
+ override val message: String
+) : Exception()
diff --git a/src/main/kotlin/de/livepoll/api/exception/UserExistsException.kt b/src/main/kotlin/de/livepoll/api/exception/UserExistsException.kt
index f9d4f4a3..faef576e 100644
--- a/src/main/kotlin/de/livepoll/api/exception/UserExistsException.kt
+++ b/src/main/kotlin/de/livepoll/api/exception/UserExistsException.kt
@@ -1,5 +1,5 @@
package de.livepoll.api.exception
class UserExistsException(
- override val message: String
-) : Exception()
\ No newline at end of file
+ override val message: String
+) : Exception()
diff --git a/src/main/kotlin/de/livepoll/api/repository/MultipleChoiceItemRepository.kt b/src/main/kotlin/de/livepoll/api/repository/MultipleChoiceItemRepository.kt
index 75f8ac00..0d873a67 100644
--- a/src/main/kotlin/de/livepoll/api/repository/MultipleChoiceItemRepository.kt
+++ b/src/main/kotlin/de/livepoll/api/repository/MultipleChoiceItemRepository.kt
@@ -4,4 +4,4 @@ import de.livepoll.api.entity.db.MultipleChoiceItem
import javax.transaction.Transactional
@Transactional
-interface MultipleChoiceItemRepository: PollItemRepository
\ No newline at end of file
+interface MultipleChoiceItemRepository : PollItemRepository
diff --git a/src/main/kotlin/de/livepoll/api/repository/UserAttrRepository.kt b/src/main/kotlin/de/livepoll/api/repository/UserAttrRepository.kt
index de07a067..3bc2dcd3 100644
--- a/src/main/kotlin/de/livepoll/api/repository/UserAttrRepository.kt
+++ b/src/main/kotlin/de/livepoll/api/repository/UserAttrRepository.kt
@@ -5,4 +5,4 @@ import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository
@Repository
-interface UserAttrRepository: JpaRepository
\ No newline at end of file
+interface UserAttrRepository : JpaRepository
diff --git a/src/main/kotlin/de/livepoll/api/repository/UserRepository.kt b/src/main/kotlin/de/livepoll/api/repository/UserRepository.kt
index e2706327..0b5301c9 100644
--- a/src/main/kotlin/de/livepoll/api/repository/UserRepository.kt
+++ b/src/main/kotlin/de/livepoll/api/repository/UserRepository.kt
@@ -5,7 +5,7 @@ import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository
@Repository
-interface UserRepository: JpaRepository {
+interface UserRepository : JpaRepository {
// @Query("SELECT u FROM User u WHERE u.username = ?1")
fun findByUsername(username: String): User?
diff --git a/src/main/kotlin/de/livepoll/api/repository/VerificationTokenRepository.kt b/src/main/kotlin/de/livepoll/api/repository/VerificationTokenRepository.kt
index a49240c0..68455469 100644
--- a/src/main/kotlin/de/livepoll/api/repository/VerificationTokenRepository.kt
+++ b/src/main/kotlin/de/livepoll/api/repository/VerificationTokenRepository.kt
@@ -4,8 +4,8 @@ import de.livepoll.api.entity.db.VerificationToken
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.Query
-interface VerificationTokenRepository: JpaRepository {
+interface VerificationTokenRepository : JpaRepository {
@Query("SELECT u FROM VerificationToken u WHERE u.token = ?1")
fun findByToken(token: String): VerificationToken
-}
\ No newline at end of file
+}
diff --git a/src/main/kotlin/de/livepoll/api/service/AccountService.kt b/src/main/kotlin/de/livepoll/api/service/AccountService.kt
index 2d77f88a..1bd8dcd1 100644
--- a/src/main/kotlin/de/livepoll/api/service/AccountService.kt
+++ b/src/main/kotlin/de/livepoll/api/service/AccountService.kt
@@ -13,6 +13,7 @@ import de.livepoll.api.util.jwtCookie.CookieCipher
import de.livepoll.api.util.jwtCookie.CookieUtil
import org.springframework.context.ApplicationEventPublisher
import org.springframework.dao.DataAccessException
+import org.springframework.dao.EmptyResultDataAccessException
import org.springframework.http.HttpHeaders
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
@@ -86,14 +87,19 @@ class AccountService(
* @param token the token string to confirm the new account
*/
fun confirmAccount(token: String): Boolean {
- val verificationToken = verificationTokenRepository.findByToken(token)
- if (verificationToken.expiryDate.after(Date())) {
- val user = userRepository.findByUsername(verificationToken.username)
- user?.isAccountEnabled = true
- verificationTokenRepository.delete(verificationToken)
- return true
+ try {
+ val verificationToken = verificationTokenRepository.findByToken(token)
+ if (verificationToken.expiryDate.after(Date())) {
+ val user = userRepository.findByUsername(verificationToken.username)
+ user?.isAccountEnabled = true
+ verificationTokenRepository.delete(verificationToken)
+ return true
+ }
+ return false
+ } catch (ex:EmptyResultDataAccessException) {
+ return false
}
- return false
+
}
private fun calculateExpiryDate() = Calendar.getInstance()
diff --git a/src/main/kotlin/de/livepoll/api/service/PollItemService.kt b/src/main/kotlin/de/livepoll/api/service/PollItemService.kt
index ac4a7ce5..62faec6a 100644
--- a/src/main/kotlin/de/livepoll/api/service/PollItemService.kt
+++ b/src/main/kotlin/de/livepoll/api/service/PollItemService.kt
@@ -222,7 +222,7 @@ class PollItemService {
/**
* Update the answers of a poll item according to an update list of selection options (strings).
- * This method works in-place and will adjust the poll item answers list.
+ * This method works in-place and will adjust the poll item answers list. The order is guaranteed.
*
* Requirements this algorithm has to fulfill:
*
@@ -236,13 +236,12 @@ class PollItemService {
* -> Remove the answer that contained this selection option
*/
fun updateAnswers(item: PollItemAnswerable, selectionOptionsUpdate: List) {
- val selectionOptionsExisting = item.answers.map { it.selectionOption }
// --- Example
// e indicates: "existing"
// u indicates: "update"
// selection_option_existing ["Ae", "Be", "Ce"]
- // selection_option_update ["Au", "Bu", "Du"]
- // selection_option_result ["Ae", "Be", "Du"]
+ // selection_option_update ["Au", "Du", "Bu"]
+ // selection_option_result ["Ae", "Du", "Be"]
// note that Ce is gone
// note that Ae/Be are used in favor of Au/Bu since Ae/Be might include answer counts > 0
@@ -264,35 +263,57 @@ class PollItemService {
}
}
- // --- Removing
- // Remove answer in db whose whose selection option is not included in the selection options from the update
- // Remove (selection_option_existing \ selection_option_update)
- // in the example: remove Ce
- item.answers.removeIf { !selectionOptionsUpdate.contains(it.selectionOption) }
-
- // --- Adding
- // Add selection option (wrapped as an answer) to db if it is not included in any answer from the db
- // Add (selection_option_update \ selection_option_existing)
- // in the example: add Du
- val toAddAnswers = selectionOptionsUpdate
- .filter { !selectionOptionsExisting.contains(it) }
- // wrap selection option string as answerable poll item
- .map {
- if (item is MultipleChoiceItem) {
- MultipleChoiceItemAnswer(0, item, it, 0)
- } else if (item is QuizItem) {
- QuizItemAnswer(0, item, it, false, 0) // the correct item is updated later
- } else {
- // Should never happen
- throw ResponseStatusException(
- HttpStatus.INTERNAL_SERVER_ERROR,
- "Only multiple choice and quiz items allow for answers"
- )
+ // --- Build hash map for answers
+ val existingAnswers: HashMap = HashMap()
+ for (answer in item.answers) {
+ // if we deal with a quiz item:
+ // make sure that correct options are all false here (they get updated later)
+ if (item is QuizItem) {
+ (answer as QuizItemAnswer).isCorrect = false
+ }
+
+ existingAnswers[answer.selectionOption] = answer
+ }
+
+ // --- Build updated answer list
+ val answersUpdated: MutableList = mutableListOf()
+ for (selectionOption in selectionOptionsUpdate) {
+ if (existingAnswers.containsKey(selectionOption)) {
+ // Take the existing answer
+ answersUpdated.add(existingAnswers[selectionOption]!!)
+ } else {
+ // Construct a new answer (depending on the item type)
+ when (item) {
+ is MultipleChoiceItem -> {
+ answersUpdated.add(MultipleChoiceItemAnswer(0, item, selectionOption, 0))
+ }
+ is QuizItem -> {
+ // the correct item is updated later
+ answersUpdated.add(QuizItemAnswer(0, item, selectionOption, false, 0))
+ }
+ else -> {
+ // Should never happen
+ throw ResponseStatusException(
+ HttpStatus.INTERNAL_SERVER_ERROR,
+ "Only multiple choice and quiz items allow for answers"
+ )
+ }
}
}
+ }
+
+ // --- Quiz item: First item should be correct one
+ if (answersUpdated.size > 0) {
+ if (item is QuizItem) {
+ (answersUpdated[0] as QuizItemAnswer).isCorrect = true
+ }
+ }
+
+ // --- Override poll item answer list
+ item.answers.clear()
// Don't know why Kotlin wants a Collection here. Might be a bug in Kotlin.
// If you know a better approach, please fix this.
- item.answers.addAll(toAddAnswers as Collection)
+ item.answers.addAll(answersUpdated as Collection)
}
/**
@@ -314,8 +335,8 @@ class PollItemService {
this.allowMultipleAnswers = pollItem.allowMultipleAnswers
this.allowBlankField = pollItem.allowBlankField
movePollItem(this.position, pollItem.position, this.poll.pollItems)
- pollRepository.saveAndFlush(this.poll)
+ pollRepository.saveAndFlush(this.poll)
return pollItemRepository.saveAndFlush(this).toDtoOut()
}
}
@@ -339,16 +360,11 @@ class PollItemService {
.orElseThrow { ResponseStatusException(HttpStatus.NOT_FOUND, "Poll item not found") }
.run {
updateAnswers(this, pollItem.selectionOptions)
-
- // Update correct answer
- val newCorrectIndex = this.answers.indexOfFirst { it.selectionOption == pollItem.selectionOptions[0] }
- this.answers[newCorrectIndex].isCorrect = true
-
this.question = pollItem.question
movePollItem(this.position, pollItem.position, this.poll.pollItems)
- pollRepository.saveAndFlush(this.poll)
- return quizItemRepository.saveAndFlush(this).toDtoOut()
+ pollRepository.saveAndFlush(this.poll)
+ return pollItemRepository.saveAndFlush(this).toDtoOut()
}
}
@@ -368,9 +384,9 @@ class PollItemService {
}
this.question = pollItem.question
movePollItem(this.position, pollItem.position, this.poll.pollItems)
- pollRepository.saveAndFlush(this.poll)
- return openTextItemRepository.saveAndFlush(this).toDtoOut()
+ pollRepository.saveAndFlush(this.poll)
+ return pollItemRepository.saveAndFlush(this).toDtoOut()
}
}
diff --git a/src/main/kotlin/de/livepoll/api/service/PollService.kt b/src/main/kotlin/de/livepoll/api/service/PollService.kt
index 1a8109f2..989d297c 100644
--- a/src/main/kotlin/de/livepoll/api/service/PollService.kt
+++ b/src/main/kotlin/de/livepoll/api/service/PollService.kt
@@ -7,7 +7,7 @@ import de.livepoll.api.entity.dto.PollDtoOut
import de.livepoll.api.entity.dto.PollItemDtoOut
import de.livepoll.api.repository.PollRepository
import de.livepoll.api.repository.UserRepository
-import de.livepoll.api.util.quartz.JobScheduleCrator
+import de.livepoll.api.util.quartz.JobScheduleCreator
import de.livepoll.api.util.quartz.StartPollPresentationJob
import de.livepoll.api.util.quartz.StopPollPresentationJob
import de.livepoll.api.util.toDtoOut
@@ -29,7 +29,7 @@ class PollService(
private val pollItemService: PollItemService,
private val webSocketService: WebSocketService,
private val schedulerFactory: SchedulerFactoryBean,
- private val jobScheduleCrator: JobScheduleCrator
+ private val jobScheduleCreator: JobScheduleCreator
) {
@@ -149,14 +149,25 @@ class PollService(
this.name = poll.name
this.currentItem = poll.currentItem
- if (poll.startDate != null && poll.endDate != null) {
- updateScheduledPoll(pollId, poll.startDate, poll.endDate)
- this.startDate = poll.startDate
- this.endDate = poll.endDate
- } else if (poll.startDate == null && poll.endDate == null) {
- stopScheduledPoll(pollId)
- this.startDate = null
- this.endDate = null
+ try {
+ if (poll.startDate == null) {
+ this.startDate = null
+ stopScheduledPoll("start-poll-$pollId")
+ } else {
+ updateScheduledPollStart(pollId, poll.startDate)
+ this.startDate = poll.startDate
+ }
+
+ if (poll.endDate == null) {
+ this.endDate = null
+ stopScheduledPoll("stop-poll-$pollId")
+ } else {
+ updateScheduledPollEnd(pollId, poll.endDate)
+ this.endDate = poll.endDate
+ }
+ } catch (ex: ResponseStatusException) {
+ pollRepository.saveAndFlush(this)
+ throw ResponseStatusException(HttpStatus.CONFLICT, ex.message)
}
if (poll.slug != null && this.slug != poll.slug) {
@@ -169,10 +180,10 @@ class PollService(
}
if (this.currentItem != null) {
- webSocketService.sendCurrentItem(this.slug, this.id, this.currentItem)
webSocketService.sendItemWithAnswers(this.currentItem!!)
}
+ webSocketService.sendCurrentItem(this.slug, this.id, this.currentItem)
return pollRepository.saveAndFlush(this).toDtoOut()
}
}
@@ -215,58 +226,92 @@ class PollService(
*
* @param pollId the id of the poll that should be scheduled
* @param startDate the start date of the poll
- * @param stopDate the end date of the poll
+ * @param endDate the end date of the poll
*/
- private fun schedulePoll(pollId: Long, startDate: Date, stopDate: Date) {
- if (startDate.before(GregorianCalendar.getInstance().time) || stopDate.before(GregorianCalendar.getInstance().time)) {
- throw ResponseStatusException(
- HttpStatus.CONFLICT,
- "Poll was not planned because start or end date is in the past"
- )
- } else {
- val jobDetailStart = jobScheduleCrator.createJob(
- StartPollPresentationJob::class.java,
- "start-poll-$pollId",
- pollId
- )
- val triggerStart =
- jobScheduleCrator.createSimpleTrigger("start-poll-trigger-$pollId", startDate)
- schedulerFactory.`object`!!.scheduleJob(jobDetailStart, triggerStart)
-
- val jobDetailStop = jobScheduleCrator.createJob(
- StopPollPresentationJob::class.java,
- "stop-poll-$pollId",
- pollId
- )
- val triggerStop = jobScheduleCrator.createSimpleTrigger("stop-poll-trigger-$pollId", stopDate)
- schedulerFactory.`object`!!.scheduleJob(jobDetailStop, triggerStop)
- }
+ private fun schedulePoll(pollId: Long, startDate: Date, endDate: Date) {
+ checkIfDateIsValid(startDate)
+ checkIfDateIsValid(endDate)
+ schedulePollStart(pollId, startDate)
+ schedulePollEnd(pollId, endDate)
}
/**
- * Update the start and end date of an poll which has already been planned.
+ * Internal method to create start event.
*
* @param pollId the id of the poll that should be scheduled
* @param startDate the start date of the poll
- * @param stopDate the end date of the poll
*/
- fun updateScheduledPoll(pollId: Long, startDate: Date, stopDate: Date) {
- if (startDate.before(GregorianCalendar.getInstance().time) || stopDate.before(GregorianCalendar.getInstance().time)) {
+ private fun schedulePollStart(pollId: Long, startDate: Date) {
+ val jobDetailStart = jobScheduleCreator.createJob(
+ StartPollPresentationJob::class.java,
+ "start-poll-$pollId",
+ pollId
+ )
+ val triggerStart =
+ jobScheduleCreator.createSimpleTrigger("start-poll-trigger-$pollId", startDate)
+ schedulerFactory.`object`!!.scheduleJob(jobDetailStart, triggerStart)
+ }
+
+ /**
+ * Internal method to create end event.
+ *
+ * @param pollId the id of the poll whose ending should be scheduled
+ * @param endDate the end date of the poll
+ */
+ private fun schedulePollEnd(pollId: Long, endDate: Date) {
+ val jobDetailStop = jobScheduleCreator.createJob(
+ StopPollPresentationJob::class.java,
+ "stop-poll-$pollId",
+ pollId
+ )
+ val triggerStop = jobScheduleCreator.createSimpleTrigger("stop-poll-trigger-$pollId", endDate)
+ schedulerFactory.`object`!!.scheduleJob(jobDetailStop, triggerStop)
+ }
+
+ /**
+ * Update the start date of a poll which has already been planned.
+ *
+ * @param pollId the id of the poll that should be scheduled
+ * @param startDate the new start date of the poll
+ */
+ private fun updateScheduledPollStart(pollId: Long, startDate: Date) {
+ checkIfDateIsValid(startDate)
+ val jobNameStart = "start-poll-trigger-$pollId"
+ val triggerStart = jobScheduleCreator.createSimpleTrigger(jobNameStart, startDate)
+ val returnDate = schedulerFactory.`object`!!.rescheduleJob(TriggerKey.triggerKey(jobNameStart), triggerStart)
+ if (returnDate == null) {
+ schedulePollStart(pollId, startDate)
+ }
+ }
+
+ /**
+ * Update the end date of a poll which has already been planned.
+ *
+ * @param pollId the id of the poll that should be scheduled
+ * @param endDate the new end date of the poll
+ */
+ private fun updateScheduledPollEnd(pollId: Long, endDate: Date) {
+ checkIfDateIsValid(endDate)
+ val jobNameStop = "stop-poll-trigger-$pollId"
+ val triggerStop = jobScheduleCreator.createSimpleTrigger(jobNameStop, endDate)
+ val returnDate = schedulerFactory.`object`!!.rescheduleJob(TriggerKey.triggerKey(jobNameStop), triggerStop)
+ if (returnDate == null) {
+ schedulePollEnd(pollId, endDate)
+ }
+ }
+
+ /**
+ * Check that the date is in the future.
+ *
+ * @param date the date
+ * @throws ResponseStatusException an exception is thrown if the date is in the past
+ */
+ private fun checkIfDateIsValid(date: Date) {
+ if (date.before(GregorianCalendar.getInstance().time)) {
throw ResponseStatusException(
HttpStatus.CONFLICT,
"Poll was not planned because start or end date is in the past"
)
- } else {
- val jobNameStart = "start-poll-trigger-$pollId"
- val jobNameStop = "stop-poll-trigger-$pollId"
- val triggerStart = jobScheduleCrator.createSimpleTrigger(jobNameStart, startDate)
- val triggerStop = jobScheduleCrator.createSimpleTrigger(jobNameStop, stopDate)
- val returnDate =
- schedulerFactory.`object`!!.rescheduleJob(TriggerKey.triggerKey(jobNameStart), triggerStart)
- schedulerFactory.`object`!!.rescheduleJob(TriggerKey.triggerKey(jobNameStop), triggerStop)
- if (returnDate == null) {
- schedulePoll(pollId, startDate, stopDate)
- }
}
}
@@ -300,11 +345,10 @@ class PollService(
/**
* Unschedule a poll.
*
- * @param pollId the id the poll which should be unscheduled
+ * @param jobkey the name of the job responsible for stopping the scheduled poll
*/
- fun stopScheduledPoll(pollId: Long) {
- schedulerFactory.`object`!!.deleteJob(JobKey("start-poll-$pollId"))
- schedulerFactory.`object`!!.deleteJob(JobKey("stop-poll-$pollId"))
+ fun stopScheduledPoll(jobkey: String) {
+ schedulerFactory.`object`!!.deleteJob(JobKey(jobkey))
}
}
diff --git a/src/main/kotlin/de/livepoll/api/util/AccountListener.kt b/src/main/kotlin/de/livepoll/api/util/AccountListener.kt
index 5ae49451..64329552 100644
--- a/src/main/kotlin/de/livepoll/api/util/AccountListener.kt
+++ b/src/main/kotlin/de/livepoll/api/util/AccountListener.kt
@@ -3,10 +3,10 @@ package de.livepoll.api.util
import de.livepoll.api.service.AccountService
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.ApplicationListener
+import org.springframework.core.io.ClassPathResource
import org.springframework.mail.javamail.JavaMailSender
import org.springframework.mail.javamail.MimeMessageHelper
import org.springframework.stereotype.Component
-import org.springframework.util.ResourceUtils
import java.util.*
import javax.mail.internet.MimeMessage
@@ -60,7 +60,7 @@ class AccountListener : ApplicationListener {
)
helper.addInline(
"logo",
- ResourceUtils.getFile("classpath:logo.png")
+ ClassPathResource("logo.png")
)
javaMailSender.send(mimeMessage)
diff --git a/src/main/kotlin/de/livepoll/api/util/CustomModelMapper.kt b/src/main/kotlin/de/livepoll/api/util/CustomModelMapper.kt
index 38f77e18..e4d7d67b 100644
--- a/src/main/kotlin/de/livepoll/api/util/CustomModelMapper.kt
+++ b/src/main/kotlin/de/livepoll/api/util/CustomModelMapper.kt
@@ -2,6 +2,8 @@ package de.livepoll.api.util
import de.livepoll.api.entity.db.*
import de.livepoll.api.entity.dto.*
+import org.springframework.http.HttpStatus
+import org.springframework.web.server.ResponseStatusException
// --------------------------------------------------- Poll mappers ----------------------------------------------------
@@ -34,8 +36,22 @@ fun MultipleChoiceItemAnswer.toDtoOut(): MultipleChoiceItemAnswerDtoOut {
}
fun QuizItem.toDtoOut(): QuizItemDtoOut {
+ // Guarantee that first item is the correct one
+ val answersSorted: MutableList = this.answers
+ val correctItemIndex = answersSorted.indexOfFirst { it.isCorrect }
+ if (correctItemIndex == -1) {
+ // Should never happen
+ val msg = "There was no correct item in the list -> data inconsistency (should NEVER happen !!!)"
+ throw ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, msg)
+ }
+
+ if (answersSorted.size > 1) {
+ // Swap
+ answersSorted[0] = answersSorted[correctItemIndex].also { answersSorted[correctItemIndex] = answersSorted[0] }
+ }
+
return QuizItemDtoOut(this.id, this.poll.id, this.question, this.position,
- "quiz", this.answers.map { it.toDtoOut() }
+ "quiz", answersSorted.map { it.toDtoOut() }
)
}
diff --git a/src/main/kotlin/de/livepoll/api/util/JwtRequestFilter.kt b/src/main/kotlin/de/livepoll/api/util/JwtRequestFilter.kt
index 540defb0..222c1822 100644
--- a/src/main/kotlin/de/livepoll/api/util/JwtRequestFilter.kt
+++ b/src/main/kotlin/de/livepoll/api/util/JwtRequestFilter.kt
@@ -16,7 +16,7 @@ import javax.servlet.http.HttpServletResponse
@Component
class JwtRequestFilter(
- private val cookieCipher: CookieCipher
+ private val cookieCipher: CookieCipher
) : OncePerRequestFilter() {
@Autowired
@@ -27,7 +27,11 @@ class JwtRequestFilter(
private val accessTokenCookieName = System.getenv("LIVE_POLL_JWT_AUTH_COOKIE_NAME")
- override fun doFilterInternal(httpServletRequest: HttpServletRequest, httpServletResponse: HttpServletResponse, filterChain: FilterChain) {
+ override fun doFilterInternal(
+ httpServletRequest: HttpServletRequest,
+ httpServletResponse: HttpServletResponse,
+ filterChain: FilterChain
+ ) {
var token: String? = null
var userName: String? = null
@@ -45,9 +49,9 @@ class JwtRequestFilter(
val userDetails: UserDetails = jwtUserDetailsService.loadUserByUsername(userName)
if (jwtUtil.validateToken(token, userDetails)) {
SecurityContextHolder.getContext().authentication =
- UsernamePasswordAuthenticationToken(userDetails, null, userDetails.authorities).apply {
- details = WebAuthenticationDetailsSource().buildDetails(httpServletRequest)
- }
+ UsernamePasswordAuthenticationToken(userDetails, null, userDetails.authorities).apply {
+ details = WebAuthenticationDetailsSource().buildDetails(httpServletRequest)
+ }
}
}
filterChain.doFilter(httpServletRequest, httpServletResponse)
diff --git a/src/main/kotlin/de/livepoll/api/util/JwtUtil.kt b/src/main/kotlin/de/livepoll/api/util/JwtUtil.kt
index 631380dd..f812f335 100644
--- a/src/main/kotlin/de/livepoll/api/util/JwtUtil.kt
+++ b/src/main/kotlin/de/livepoll/api/util/JwtUtil.kt
@@ -11,7 +11,7 @@ import java.util.function.Function
@Service
class JwtUtil(
- val blockedTokenRepository: BlockedTokenRepository
+ val blockedTokenRepository: BlockedTokenRepository
) {
private val secret = System.getenv("LIVE_POLL_JWT_SECRET")
@@ -20,12 +20,13 @@ class JwtUtil(
fun extractExpiration(token: String?): Date = extractClaim(token) { obj: Claims -> obj.expiration }
- fun extractClaim(token: String?, claimsResolver: Function) = claimsResolver.apply(extractAllClaims(token))
+ fun extractClaim(token: String?, claimsResolver: Function) =
+ claimsResolver.apply(extractAllClaims(token))
private fun extractAllClaims(token: String?): Claims {
return Jwts.parser().setSigningKey(secret)
- .parseClaimsJws(token)
- .body
+ .parseClaimsJws(token)
+ .body
}
private fun isTokenExpired(token: String?) = extractExpiration(token).before(Date())
@@ -34,14 +35,14 @@ class JwtUtil(
private fun createToken(claims: Map, subject: String): String {
return Jwts.builder()
- .setClaims(claims)
- .setSubject(subject).setIssuedAt(Date(System.currentTimeMillis()))
- .setExpiration(Date(System.currentTimeMillis() + TOKEN_DURATION * 1000))
- .signWith(SignatureAlgorithm.HS256, secret).compact()
+ .setClaims(claims)
+ .setSubject(subject).setIssuedAt(Date(System.currentTimeMillis()))
+ .setExpiration(Date(System.currentTimeMillis() + TOKEN_DURATION * 1000))
+ .signWith(SignatureAlgorithm.HS256, secret).compact()
}
- fun validateToken(token: String?, userDetails: UserDetails)
- = extractUsername(token) == userDetails.username && !isTokenExpired(token) && !isTokenBlocked(token)
+ fun validateToken(token: String?, userDetails: UserDetails) =
+ extractUsername(token) == userDetails.username && !isTokenExpired(token) && !isTokenBlocked(token)
private fun isTokenBlocked(token: String?): Boolean {
blockedTokenRepository.findByToken(token)?.run {
@@ -49,4 +50,4 @@ class JwtUtil(
}
return false
}
-}
\ No newline at end of file
+}
diff --git a/src/main/kotlin/de/livepoll/api/util/OnCreateAccountEvent.kt b/src/main/kotlin/de/livepoll/api/util/OnCreateAccountEvent.kt
index 7c411d27..d1a3258f 100644
--- a/src/main/kotlin/de/livepoll/api/util/OnCreateAccountEvent.kt
+++ b/src/main/kotlin/de/livepoll/api/util/OnCreateAccountEvent.kt
@@ -4,6 +4,6 @@ import de.livepoll.api.entity.db.User
import org.springframework.context.ApplicationEvent
class OnCreateAccountEvent(
- var user: User,
- var appUrl: String
-): ApplicationEvent(user)
\ No newline at end of file
+ var user: User,
+ var appUrl: String
+) : ApplicationEvent(user)
diff --git a/src/main/kotlin/de/livepoll/api/util/jwtCookie/CookieCipher.kt b/src/main/kotlin/de/livepoll/api/util/jwtCookie/CookieCipher.kt
index 7a35ad04..e9e9f523 100644
--- a/src/main/kotlin/de/livepoll/api/util/jwtCookie/CookieCipher.kt
+++ b/src/main/kotlin/de/livepoll/api/util/jwtCookie/CookieCipher.kt
@@ -29,7 +29,8 @@ class CookieCipher {
try {
val cipher = Cipher.getInstance("AES/ECB/PKCS5Padding")
cipher.init(Cipher.ENCRYPT_MODE, secretKey)
- return Base64.getEncoder().encodeToString(cipher.doFinal(stringToEncrypt.toByteArray(StandardCharsets.UTF_8)))
+ return Base64.getEncoder()
+ .encodeToString(cipher.doFinal(stringToEncrypt.toByteArray(StandardCharsets.UTF_8)))
} catch (e: Exception) {
e.printStackTrace()
throw Exception("Error in cookieCipher")
@@ -47,4 +48,4 @@ class CookieCipher {
throw Exception("Error in cookieCipher")
}
}
-}
\ No newline at end of file
+}
diff --git a/src/main/kotlin/de/livepoll/api/util/jwtCookie/CookieUtil.kt b/src/main/kotlin/de/livepoll/api/util/jwtCookie/CookieUtil.kt
index 703b88a0..691d850c 100644
--- a/src/main/kotlin/de/livepoll/api/util/jwtCookie/CookieUtil.kt
+++ b/src/main/kotlin/de/livepoll/api/util/jwtCookie/CookieUtil.kt
@@ -6,25 +6,25 @@ import org.springframework.stereotype.Component
@Component
class CookieUtil(
- private val cookieCipher: CookieCipher
+ private val cookieCipher: CookieCipher
) {
private val accessTokenCookieName = System.getenv("LIVE_POLL_JWT_AUTH_COOKIE_NAME")
private val isTLSEncrypted = System.getenv("LIVE_POLL_SERVER_URL").startsWith("https://")
private val isDevServer = System.getenv("LIVE_POLL_SERVER_URL").contains("localhost")
- private val domain = if(isDevServer) "localhost" else "live-poll.de"
+ private val domain = if (isDevServer) "localhost" else "live-poll.de"
fun createAccessTokenCookie(token: String) = buildCookie(cookieCipher.encrypt(token))
fun deleteAccessTokenCookie() = buildCookie("")
fun buildCookie(content: String) = ResponseCookie.from(accessTokenCookieName, content)
- .maxAge(if (content.isBlank()) 0 else TOKEN_DURATION)
- .httpOnly(true)
- // Secure is only supported with https
- .secure(isTLSEncrypted)
- // Use top level domain. Subdomains are included automatically (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie)
- .domain(domain)
- .sameSite("None")
- .path("/")
- .build()
-}
\ No newline at end of file
+ .maxAge(if (content.isBlank()) 0 else TOKEN_DURATION)
+ .httpOnly(true)
+ // Secure is only supported with https
+ .secure(isTLSEncrypted)
+ // Use top level domain. Subdomains are included automatically (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie)
+ .domain(domain)
+ .sameSite("None")
+ .path("/")
+ .build()
+}
diff --git a/src/main/kotlin/de/livepoll/api/util/quartz/JobScheduleCrator.kt b/src/main/kotlin/de/livepoll/api/util/quartz/JobScheduleCrator.kt
index bb5a8a82..d45040fc 100644
--- a/src/main/kotlin/de/livepoll/api/util/quartz/JobScheduleCrator.kt
+++ b/src/main/kotlin/de/livepoll/api/util/quartz/JobScheduleCrator.kt
@@ -12,8 +12,8 @@ import java.util.*
@Component
-class JobScheduleCrator(
- private val applicationContext: ApplicationContext
+class JobScheduleCreator(
+ private val applicationContext: ApplicationContext
) {
fun createJob(jobClass: Class, jobName: String, pollId: Long): JobDetail {
@@ -42,4 +42,4 @@ class JobScheduleCrator(
return factoryBean.getObject()!!
}
-}
\ No newline at end of file
+}
diff --git a/src/main/kotlin/de/livepoll/api/util/quartz/StartPollPresentationJob.kt b/src/main/kotlin/de/livepoll/api/util/quartz/StartPollPresentationJob.kt
index 1533724b..6e98ac59 100644
--- a/src/main/kotlin/de/livepoll/api/util/quartz/StartPollPresentationJob.kt
+++ b/src/main/kotlin/de/livepoll/api/util/quartz/StartPollPresentationJob.kt
@@ -1,8 +1,6 @@
package de.livepoll.api.util.quartz
-import de.livepoll.api.repository.PollRepository
import de.livepoll.api.service.PollService
-import de.livepoll.api.service.WebSocketService
import org.quartz.Job
import org.quartz.JobExecutionContext
import org.springframework.beans.factory.annotation.Autowired
@@ -21,4 +19,4 @@ class StartPollPresentationJob : Job {
pollService.executeStartPoll(context.jobDetail.jobDataMap["pollId"].toString().toLong())
}
-}
\ No newline at end of file
+}
diff --git a/src/main/kotlin/de/livepoll/api/util/quartz/StopPollPresentationJob.kt b/src/main/kotlin/de/livepoll/api/util/quartz/StopPollPresentationJob.kt
index 76f36aa9..a5e253fe 100644
--- a/src/main/kotlin/de/livepoll/api/util/quartz/StopPollPresentationJob.kt
+++ b/src/main/kotlin/de/livepoll/api/util/quartz/StopPollPresentationJob.kt
@@ -1,8 +1,6 @@
package de.livepoll.api.util.quartz
-import de.livepoll.api.repository.PollRepository
import de.livepoll.api.service.PollService
-import de.livepoll.api.service.WebSocketService
import org.quartz.Job
import org.quartz.JobExecutionContext
import org.springframework.beans.factory.annotation.Autowired
@@ -20,4 +18,4 @@ class StopPollPresentationJob : Job {
println("Execute stop poll event with poll-id: " + context.jobDetail.jobDataMap["pollId"].toString())
pollService.executeStopPoll(context.jobDetail.jobDataMap["pollId"].toString().toLong())
}
-}
\ No newline at end of file
+}
diff --git a/src/main/kotlin/de/livepoll/api/util/websocket/CustomWebSocketHandshakeHandler.kt b/src/main/kotlin/de/livepoll/api/util/websocket/CustomWebSocketHandshakeHandler.kt
index 0120d44f..6b3e172a 100644
--- a/src/main/kotlin/de/livepoll/api/util/websocket/CustomWebSocketHandshakeHandler.kt
+++ b/src/main/kotlin/de/livepoll/api/util/websocket/CustomWebSocketHandshakeHandler.kt
@@ -10,7 +10,11 @@ private const val ATTR_PRINCIPAL = "_principal_"
class CustomWebSocketHandshakeHandler : DefaultHandshakeHandler() {
- override fun determineUser(request: ServerHttpRequest, wsHandler: WebSocketHandler, attributes: MutableMap): Principal {
+ override fun determineUser(
+ request: ServerHttpRequest,
+ wsHandler: WebSocketHandler,
+ attributes: MutableMap
+ ): Principal {
val name: String
if (!attributes.containsKey(ATTR_PRINCIPAL)) {
name = UUID.randomUUID().toString()
@@ -21,4 +25,4 @@ class CustomWebSocketHandshakeHandler : DefaultHandshakeHandler() {
return Principal { name }
}
-}
\ No newline at end of file
+}
diff --git a/src/main/kotlin/de/livepoll/api/util/websocket/HttpHandshakeInterceptor.kt b/src/main/kotlin/de/livepoll/api/util/websocket/HttpHandshakeInterceptor.kt
index d33fb047..1c4323c2 100644
--- a/src/main/kotlin/de/livepoll/api/util/websocket/HttpHandshakeInterceptor.kt
+++ b/src/main/kotlin/de/livepoll/api/util/websocket/HttpHandshakeInterceptor.kt
@@ -8,7 +8,12 @@ import org.springframework.web.socket.server.HandshakeInterceptor
class HttpHandshakeInterceptor : HandshakeInterceptor {
- override fun beforeHandshake(request: ServerHttpRequest, response: ServerHttpResponse, webSocketHandler: WebSocketHandler, attributes: MutableMap): Boolean {
+ override fun beforeHandshake(
+ request: ServerHttpRequest,
+ response: ServerHttpResponse,
+ webSocketHandler: WebSocketHandler,
+ attributes: MutableMap
+ ): Boolean {
if (request is ServletServerHttpRequest) {
val session = request.servletRequest.session
attributes["sessionId"] = session.id
@@ -20,4 +25,4 @@ class HttpHandshakeInterceptor : HandshakeInterceptor {
override fun afterHandshake(p0: ServerHttpRequest, p1: ServerHttpResponse, p2: WebSocketHandler, p3: Exception?) {
}
-}
\ No newline at end of file
+}
diff --git a/src/main/kotlin/de/livepoll/api/util/websocket/SubscribeListener.kt b/src/main/kotlin/de/livepoll/api/util/websocket/SubscribeListener.kt
index 479829cc..36e57847 100644
--- a/src/main/kotlin/de/livepoll/api/util/websocket/SubscribeListener.kt
+++ b/src/main/kotlin/de/livepoll/api/util/websocket/SubscribeListener.kt
@@ -13,10 +13,10 @@ import org.springframework.web.socket.messaging.SessionSubscribeEvent
@Component
class SubscribeListener(
- private val messagingTemplate: SimpMessageSendingOperations,
- private val pollRepository: PollRepository,
- private val pollItemService: PollItemService,
- private val webSocketService: WebSocketService
+ private val messagingTemplate: SimpMessageSendingOperations,
+ private val pollRepository: PollRepository,
+ private val pollItemService: PollItemService,
+ private val webSocketService: WebSocketService
) : ApplicationListener {
@Transactional
diff --git a/src/main/resources/quartz.properties b/src/main/resources/quartz.properties
index 1a1e4b6a..da1a4988 100644
--- a/src/main/resources/quartz.properties
+++ b/src/main/resources/quartz.properties
@@ -2,7 +2,6 @@
org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount=2
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true
-
# JDBCJobStore using JobStoreTX
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
diff --git a/src/test/kotlin/de/livepoll/api/controller/v1/PollItemServiceTest.kt b/src/test/kotlin/de/livepoll/api/controller/v1/PollItemServiceTest.kt
index ce116247..e1064398 100644
--- a/src/test/kotlin/de/livepoll/api/controller/v1/PollItemServiceTest.kt
+++ b/src/test/kotlin/de/livepoll/api/controller/v1/PollItemServiceTest.kt
@@ -18,12 +18,9 @@ import org.springframework.boot.test.context.TestConfiguration
import org.springframework.boot.test.mock.mockito.MockBean
import org.springframework.context.annotation.Bean
import org.springframework.test.context.ActiveProfiles
-import org.springframework.test.context.TestPropertySource
import org.springframework.test.context.junit4.SpringRunner
import java.util.*
-
-
@RunWith(SpringRunner::class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK, classes = [LivePollApplication::class])
@AutoConfigureMockMvc
@@ -55,7 +52,7 @@ class PollItemServiceTest {
@MockBean
private lateinit var quizItemAnswerRepository: QuizItemAnswerRepository
- init{
+ init {
}
@@ -133,7 +130,7 @@ class PollItemServiceTest {
0,
mockPoll,
0,
- "Multiple Choice Question1",
+ "Multiple Choice Question 1",
allowMultipleAnswers = false,
allowBlankField = false,
answers = mutableListOf()
@@ -178,7 +175,7 @@ class PollItemServiceTest {
val multipleChoice1 = MultipleChoiceItemDtoOut(
0,
mockPoll.id,
- "Multiple Choice Question1",
+ "Multiple Choice Question 1",
0,
PollItemType.MULTIPLE_CHOICE.representation,
answers1
@@ -252,7 +249,7 @@ class PollItemServiceTest {
val answer21 = QuizItemAnswerDtoOut(4, "Option 2-1", false, 0)
val answer22 = QuizItemAnswerDtoOut(5, "Option 2-2", true, 0)
- val answers2 = listOf(answer21, answer22)
+ val answers2 = listOf(answer22, answer21)
// Shell
val quiz1 = QuizItemDtoOut(
diff --git a/src/test/kotlin/de/livepoll/api/cucumber/CucumberIntegrationTest.kt b/src/test/kotlin/de/livepoll/api/cucumber/CucumberIntegrationTest.kt
index 8f3a15b5..e3a335f1 100644
--- a/src/test/kotlin/de/livepoll/api/cucumber/CucumberIntegrationTest.kt
+++ b/src/test/kotlin/de/livepoll/api/cucumber/CucumberIntegrationTest.kt
@@ -16,7 +16,6 @@ import org.springframework.http.*
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.test.context.ActiveProfiles
-import org.springframework.test.context.TestPropertySource
import org.springframework.web.client.RestTemplate
import org.springframework.web.client.exchange
import java.security.cert.X509Certificate
@@ -28,7 +27,7 @@ import javax.net.ssl.SSLContext
@SpringBootTest(classes = [LivePollApplication::class], webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("test")
class CucumberIntegrationTest(
- private val userRepository: UserRepository
+ private val userRepository: UserRepository
) {
@Autowired
@@ -62,14 +61,14 @@ class CucumberIntegrationTest(
val acceptingTrustStrategy = { chain: Array?, authType: String? -> true }
val sslContext: SSLContext = org.apache.http.ssl.SSLContexts.custom()
- .loadTrustMaterial(null, acceptingTrustStrategy)
- .build()
+ .loadTrustMaterial(null, acceptingTrustStrategy)
+ .build()
val csf = SSLConnectionSocketFactory(sslContext)
val httpClient: CloseableHttpClient = HttpClients.custom()
- .setSSLSocketFactory(csf)
- .build()
+ .setSSLSocketFactory(csf)
+ .build()
val requestFactory = HttpComponentsClientHttpRequestFactory()
@@ -79,7 +78,17 @@ class CucumberIntegrationTest(
protected fun logInWithTestUser(): Pair {
if (userRepository.findByUsername(testUserName) == null) {
- userRepository.saveAndFlush(User(0, testUserName, "email", passwordEncoder.encode(testUserPassword), true, "ROLE_USER", emptyList()))
+ userRepository.saveAndFlush(
+ User(
+ 0,
+ testUserName,
+ "email",
+ passwordEncoder.encode(testUserPassword),
+ true,
+ "ROLE_USER",
+ emptyList()
+ )
+ )
testUser = userRepository.findByUsername(testUserName)
}
// https://springbootdev.com/2017/11/21/spring-resttemplate-exchange-method/
@@ -92,9 +101,9 @@ class CucumberIntegrationTest(
// make request
val responseEntity: ResponseEntity = restTemplate.exchange(
- url,
- HttpMethod.POST,
- requestEntity
+ url,
+ HttpMethod.POST,
+ requestEntity
)
// store session cookie (which includes the JWT token)
@@ -103,7 +112,10 @@ class CucumberIntegrationTest(
return Pair(responseEntity.statusCode, setCookie!!)
}
- protected final inline fun makeGetRequestWithSessionCookie(url: String, sessionCookie: String): ResponseEntity {
+ protected final inline fun makeGetRequestWithSessionCookie(
+ url: String,
+ sessionCookie: String
+ ): ResponseEntity {
// request body params & headers
val headers = HttpHeaders()
headers["Cookie"] = sessionCookie
@@ -111,9 +123,9 @@ class CucumberIntegrationTest(
// make request
return restTemplate.exchange(
- url,
- HttpMethod.GET,
- requestEntity
+ url,
+ HttpMethod.GET,
+ requestEntity
)
}
diff --git a/src/test/kotlin/de/livepoll/api/cucumber/stepdefinitions/PollStepDefinitions.kt b/src/test/kotlin/de/livepoll/api/cucumber/stepdefinitions/PollStepDefinitions.kt
index eb8c342b..d0497bb2 100644
--- a/src/test/kotlin/de/livepoll/api/cucumber/stepdefinitions/PollStepDefinitions.kt
+++ b/src/test/kotlin/de/livepoll/api/cucumber/stepdefinitions/PollStepDefinitions.kt
@@ -15,8 +15,8 @@ import org.springframework.web.client.exchange
import java.sql.Date
class PollStepDefinitions(
- userRepository: UserRepository,
- private val pollRepository: PollRepository
+ userRepository: UserRepository,
+ private val pollRepository: PollRepository
) : CucumberIntegrationTest(userRepository) {
private val POLL_ENDPOINT = "/v1/polls"
@@ -48,9 +48,9 @@ class PollStepDefinitions(
// make request
val responseEntity: ResponseEntity = restTemplate.exchange(
- url,
- HttpMethod.POST,
- requestEntity
+ url,
+ HttpMethod.POST,
+ requestEntity
)
assertThat(responseEntity.statusCode).isEqualTo(HttpStatus.CREATED)
@@ -65,7 +65,7 @@ class PollStepDefinitions(
fun retrieveMyPolls() {
val url = "${SERVER_URL}:$port$POLL_ENDPOINT"
val pollResponseEntity =
- makeGetRequestWithSessionCookie>(url, SessionCookieUtil.sessionCookie)
+ makeGetRequestWithSessionCookie>(url, SessionCookieUtil.sessionCookie)
assertThat(pollResponseEntity.statusCode).isEqualTo(HttpStatus.OK)
assertThat(pollResponseEntity.body).isNotNull
for (poll in pollResponseEntity.body!!) {
diff --git a/src/test/kotlin/de/livepoll/api/cucumber/stepdefinitions/UserStepDefinitions.kt b/src/test/kotlin/de/livepoll/api/cucumber/stepdefinitions/UserStepDefinitions.kt
index 56ad5e1b..eaed7de2 100644
--- a/src/test/kotlin/de/livepoll/api/cucumber/stepdefinitions/UserStepDefinitions.kt
+++ b/src/test/kotlin/de/livepoll/api/cucumber/stepdefinitions/UserStepDefinitions.kt
@@ -1,9 +1,11 @@
package de.livepoll.api.cucumber.stepdefinitions
import de.livepoll.api.cucumber.CucumberIntegrationTest
+import de.livepoll.api.entity.db.OpenTextItem
import de.livepoll.api.entity.db.Poll
import de.livepoll.api.entity.db.PollItem
import de.livepoll.api.entity.db.User
+import de.livepoll.api.repository.OpenTextItemRepository
import de.livepoll.api.repository.PollRepository
import de.livepoll.api.repository.UserRepository
import io.cucumber.java.en.And
@@ -19,12 +21,14 @@ import java.util.*
private const val LOGOUT_ENDPOINT = "/v1/account/logout"
class UserStepDefinitions(
- private val pollRepository: PollRepository,
- private val userRepository: UserRepository
+ private val pollRepository: PollRepository,
+ private val userRepository: UserRepository,
+ private val openTextItemRepository: OpenTextItemRepository
) : CucumberIntegrationTest(userRepository) {
private val USER_ENDPOINT = "/v1/user"
private val POLL_ENDPOINT = "/v1/polls"
+ private val POLL_ITEM_ENDPOINT = "/v1/poll-items"
lateinit var status: HttpStatus
var alreadyConfirmed = false
@@ -60,14 +64,36 @@ class UserStepDefinitions(
@And("I am not authorized to retrieve information about a different user")
fun getInfoAboutDifferentUser() {
- if(userRepository.findByUsername("different_user") == null){
- userRepository.saveAndFlush(User(1,"different_user", "different_email", passwordEncoder.encode("12345"), true, "ROLE_USER", emptyList()))
+ if (userRepository.findByUsername("different_user") == null) {
+ userRepository.saveAndFlush(
+ User(
+ 1,
+ "different_user",
+ "different_email",
+ passwordEncoder.encode("12345"),
+ true,
+ "ROLE_USER",
+ emptyList()
+ )
+ )
}
- if(pollRepository.findBySlug("different_user_test_slug") == null){
- pollRepository.saveAndFlush(Poll(0, userRepository.findByUsername("different_user")!!, "different_user_test_poll", GregorianCalendar(2021, 5, 2).time, GregorianCalendar(2021, 5, 2).time, "different_user_test_slug", null, emptyList().toMutableList()))
+ if (pollRepository.findBySlug("different_user_test_slug") == null) {
+ pollRepository.saveAndFlush(
+ Poll(
+ 0,
+ userRepository.findByUsername("different_user")!!,
+ "different_user_test_poll",
+ GregorianCalendar(2021, 5, 2).time,
+ GregorianCalendar(2021, 5, 2).time,
+ "different_user_test_slug",
+ null,
+ emptyList().toMutableList()
+ )
+ )
}
- val id = pollRepository.findBySlug("different_user_test_slug")!!.id
- val url = "${SERVER_URL}:$port$POLL_ENDPOINT/${id}"
+ val poll = pollRepository.findBySlug("different_user_test_slug")!!
+ val id = openTextItemRepository.saveAndFlush(OpenTextItem(0, poll, "question", 0, mutableListOf())).id
+ val url = "${SERVER_URL}:$port$POLL_ITEM_ENDPOINT/${id}"
try {
val pollResponseEntity = makeGetRequestWithSessionCookie(url, SessionCookieUtil.sessionCookie)
assertThat(pollResponseEntity.statusCode).isEqualTo(HttpStatus.FORBIDDEN)
@@ -86,9 +112,9 @@ class UserStepDefinitions(
// make request
val logOutResponseEntity: ResponseEntity = restTemplate.exchange(
- url,
- HttpMethod.PUT,
- logOutRequestEntity
+ url,
+ HttpMethod.PUT,
+ logOutRequestEntity
)
assertThat(logOutResponseEntity.statusCode).isEqualTo(HttpStatus.OK)
}
diff --git a/src/test/resources/quartz.properties b/src/test/resources/quartz.properties
index 1a1e4b6a..da1a4988 100644
--- a/src/test/resources/quartz.properties
+++ b/src/test/resources/quartz.properties
@@ -2,7 +2,6 @@
org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount=2
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true
-
# JDBCJobStore using JobStoreTX
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate