+## 🪄 $\textsf{\color{salmon}Getting started}$
-## How to
-### Build & Deploy
-This application server will is hosted by
in a Docker container and a PostgreSQL database hosted by Neon. Additionally a
is set up to keep the service alive on Render due to 15-min inactivity on free tier plan.
-The development process is structured by the TDD (test driven development) principle.
+### $\textsf{\color{teal}Prerequisites}$
+- node: v22+
+- PostgreSQL v17+ (local or hosted like Neon)
+- Docker v4.54+
+- Cloudflare R2 bucket (file handling)
+- Betterstack Telemetry (logging)
+### $\textsf{\color{teal}Local setup}$
+Download or clone project
+```sh
+git clone https://github.com/yqni13/support
+```
+Create new .env file and fill in your credentials/other env data [(see docs)](./docs/CONFIGURATION.md).
+Navigate/cd into project directory ./backend and install dependencies via npm
+```sh
+npm ci
+```
+Run migrations [(see docs)](./docs/MIGRATION.md).
+Start application in local (development) environment:
+```sh
+npm run start:dev
+```
+Alternatively run application in Docker container [(see docs)](./docs/DEVOPS.md).
-### $\textsf{\color{teal}Tickets}$
-
-Documentation follows with finished refactoring (task: SUPPORT-65).
+## 🧩 $\textsf{\color{salmon}Features}$
+| Feature | Description |
+|---------|-------------|
+| 🪲 Ticket system | Handles support & bug reports per client with status lifecycle and optional file attachments |
+| ✨ Feedback & Rating system | Abuse-resistant rating system with atomic aggregate updates - one active rating per user per client |
+| 📂 Cloud file handling | Upload/delete via Cloudflare R2 (S3-compatible) - supporting pdf & images up to 1MB each, max 5 per ticket |
+| 🔎 Filtered search | Query ticket and user data by properties and/or timespan |
+| 🔐 Maintenance Mode | Enable/disable application triggered by request or internal logic |
+| 🕵️ Rate limiting | Request throttling with violation handling |
+| 🔑 API Key Auth | Client authentication via API keys |
-### $\textsf{\color{teal}Feedback/Rating}$
+### $\textsf{\color{teal}Feedback \&\ Rating}$
-Documentation follows with finished refactoring (task: SUPPORT-65).
+User can utilize a feedback & rating system to rate the application in use and send criticism or praise. For every client can exist multiple entries for the entity `Feedback` but only one `FeedbackRating` which holds the accumulated data of the pointing feedback entries.
+Resubmissions are handled in the database by an `ON CONFLICT` upsert query [see upsertInTa()](./backend/src/repositories/feedback.repository.ts) on the unique `(client_id, user_id)` constraint, followed by an atomic aggregate update to the 'FeedbackRating' table entry. Both queries are executed within a single transaction to guarantee data consistency.
+The rating happens numerical (1-5) and returns an average rating value as number with up to 1 decimal place.
+Furthermore, if an existing feedback entry has a message stored, but is not reviewed, the feedback gets NOT updated and request throws a specific exception.
### $\textsf{\color{teal}File handling}$
-User can attach files for any support/bug ticket to provide further information (screenshots, images, ...) on their message. Attachments are limited to upload up to `5` files and each file can be up to `1`mb [see validation](./backend/src/middleware/files/validate.files.middleware.ts). Currently only `images` (webp, jpg, jpeg, png) and `pdf` files are supported, but more will follow. Cloud in use is `Cloudflare` (see Figure 1) using S3Client for api communication and files will be deleted when a ticket is closed, canceled or expired (time check).
+User can attach files for any support/bug ticket to provide further information (screenshots, images, ...) on their message. Attachments are limited to upload up to `5` files and each file can be up to `1`MB [see validation](./backend/src/middleware/files/validate.files.middleware.ts). Currently only `images` (webp, jpg, jpeg, png) and `pdf` files are supported, but more will follow. Cloud in use is `Cloudflare` (see Figure 1) using S3Client for api communication and files will be deleted when a ticket is closed, canceled or expired (time check).

Figure 1 - Cloudflare upload demo, v1.0.0
@@ -75,7 +83,7 @@ User can attach files for any support/bug ticket to provide further information
-### $\textsf{\color{teal}Logging}$
+## 📝 $\textsf{\color{salmon}Logging}$
To monitor errors the logging framework `Winston` is used in combination with Logtail from `Betterstack` as a Singleton: [config](./backend/src/logger/config.logger.ts)
While working within local (DEV) or test environment, error messages are logged into the consoles. For the deployed environments (STAG/PROD) the logging is set to send logtails to Betterstack (longer storage time than app-hosting service). For easy access and monitoring of error messages, the Betterstack UI client dashboard comes in handy (see Figure 2). Additional meta data (environment + version numbers) help identifying and assigning errors.
@@ -86,7 +94,7 @@ To monitor errors the logging framework `Winston` is used in combination with Lo
-## Testing
+## 🔧 $\textsf{\color{salmon}Testing}$
### $\textsf{\color{teal}Demo}$
@@ -125,7 +133,8 @@ Install the packages `@jest/globals`, `@types/jest`, `supertest`, `@testcontaine
```sh
npm install jest @jest/globals @types/jest supertest @testcontainers/postgresql testcontainers --save-dev
```
-450+ tests exist currently for models, utils, validators and workflows (integration tests) - see [tests](./backend/tests).
+`450+ tests` exist currently for models, utils, validators and workflows (integration tests) - [see tests](./backend/tests).
+Integration-Tests can only run with `active Docker service` due to the ephemeral (temporary) database by testcontainers.
Run tests on local device by including setup for dotenv/config to provide environment variables:
```sh
set NODE_ENV=test && jest --setupFiles dotenv/config
@@ -155,22 +164,22 @@ Preventing an unwanted merge with unfinished/failed test run, the project is set
-## Updates
+## 📈 $\textsf{\color{salmon}Updates}$
[see changelog for all updates](/docs/CHANGELOG.md)
-### $\textsf{\color{forestgreen}last update:}$
-$\textsf{[v1.3.5\ =>\ {\textbf{\color{brown}v1.4.1}]}}$ app
-$\textsf{[v1.5.4\ =>\ {\textbf{\color{brown}v1.6.0}]}}$ database
-- $\textsf{\color{teal}Addition:}$ Added api route + logic for Feedback/FeedbackRating to add new feedback/rating or get current average rating value.
+$\textsf{[v1.4.1\ =>\ {\textbf{\color{brown}v1.4.4}]}}$ app
- $\textsf{\color{orange}Patch:}$ Updated:
- + testing whole process (request-to-response) with individual injection of middlewares when necessary.
- + database with new migration to add tables 'feedback_entries' and 'feedback_ratings' to handle single feedback/ratings seperately from accumulated average rating.
+ + entity ID's are using now nominal types instead basic string|number.
+ + some model functions are renamed to keep consistency and improve readability.
+ + some api routes have been shortened to keep consistency and improve readability.
### Update objectives:
+ - - caching layer
+ - - background worker
- - jenkins setup
- - mail setup
\ No newline at end of file
diff --git a/assets/img/logo.png b/assets/img/logo.png
deleted file mode 100644
index 760b6c9..0000000
Binary files a/assets/img/logo.png and /dev/null differ
diff --git a/assets/img/readme-bg.png b/assets/img/readme-bg.png
deleted file mode 100644
index 22cc550..0000000
Binary files a/assets/img/readme-bg.png and /dev/null differ
diff --git a/backend/package-lock.json b/backend/package-lock.json
index ab057c9..10d1d07 100644
--- a/backend/package-lock.json
+++ b/backend/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "support_backend",
- "version": "1.4.1",
+ "version": "1.4.4",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "support_backend",
- "version": "1.2.1",
+ "version": "1.4.4",
"license": "ISC",
"dependencies": {
"@aws-sdk/client-s3": "^3.985.0",
@@ -935,13 +935,13 @@
}
},
"node_modules/@aws-sdk/xml-builder": {
- "version": "3.972.4",
- "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.4.tgz",
- "integrity": "sha512-0zJ05ANfYqI6+rGqj8samZBFod0dPPousBjLEqg8WdxSgbMAkRgLyn81lP215Do0rFJ/17LIXwr7q0yK24mP6Q==",
+ "version": "3.972.10",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.10.tgz",
+ "integrity": "sha512-OnejAIVD+CxzyAUrVic7lG+3QRltyja9LoNqCE/1YVs8ichoTbJlVSaZ9iSMcnHLyzrSNtvaOGjSDRP+d/ouFA==",
"license": "Apache-2.0",
"dependencies": {
- "@smithy/types": "^4.12.0",
- "fast-xml-parser": "5.3.4",
+ "@smithy/types": "^4.13.0",
+ "fast-xml-parser": "5.4.1",
"tslib": "^2.6.2"
},
"engines": {
@@ -1620,27 +1620,6 @@
"node": ">=6"
}
},
- "node_modules/@isaacs/balanced-match": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz",
- "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==",
- "license": "MIT",
- "engines": {
- "node": "20 || >=22"
- }
- },
- "node_modules/@isaacs/brace-expansion": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.1.tgz",
- "integrity": "sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ==",
- "license": "MIT",
- "dependencies": {
- "@isaacs/balanced-match": "^4.0.1"
- },
- "engines": {
- "node": "20 || >=22"
- }
- },
"node_modules/@isaacs/cliui": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
@@ -2851,9 +2830,9 @@
}
},
"node_modules/@smithy/types": {
- "version": "4.12.0",
- "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.12.0.tgz",
- "integrity": "sha512-9YcuJVTOBDjg9LWo23Qp0lTQ3D7fQsQtwle0jVfpbUHy9qBwCEgKuVH4FqFB3VYu0nwdHKiEMA+oXz7oV8X1kw==",
+ "version": "4.13.0",
+ "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.13.0.tgz",
+ "integrity": "sha512-COuLsZILbbQsdrwKQpkkpyep7lCsByxwj7m0Mg5v66/ZTyenlfBc40/QFQ5chO0YN/PNEH1Bi3fGtfXPnYNeDw==",
"license": "Apache-2.0",
"dependencies": {
"tslib": "^2.6.2"
@@ -4116,13 +4095,13 @@
"license": "MIT"
},
"node_modules/axios": {
- "version": "1.12.2",
- "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz",
- "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==",
+ "version": "1.13.6",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz",
+ "integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==",
"license": "MIT",
"dependencies": {
- "follow-redirects": "^1.15.6",
- "form-data": "^4.0.4",
+ "follow-redirects": "^1.15.11",
+ "form-data": "^4.0.5",
"proxy-from-env": "^1.1.0"
}
},
@@ -5920,10 +5899,25 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/fast-xml-builder": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.0.tgz",
+ "integrity": "sha512-7mtITW/we2/wTUZqMyBOR2F8xP4CRxMiSEcQxPIqdRWdO2L/HZSOlzoNyghmyDwNB8BDxePooV1ZTJpkOUhdRg==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "path-expression-matcher": "^1.1.2"
+ }
+ },
"node_modules/fast-xml-parser": {
- "version": "5.3.4",
- "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.3.4.tgz",
- "integrity": "sha512-EFd6afGmXlCx8H8WTZHhAoDaWaGyuIBoZJ2mknrNxug+aZKjkp0a0dlars9Izl+jF+7Gu1/5f/2h68cQpe0IiA==",
+ "version": "5.4.1",
+ "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.4.1.tgz",
+ "integrity": "sha512-BQ30U1mKkvXQXXkAGcuyUA/GA26oEB7NzOtsxCDtyu62sjGw5QraKFhx2Em3WQNjPw9PG6MQ9yuIIgkSDfGu5A==",
"funding": [
{
"type": "github",
@@ -5932,7 +5926,8 @@
],
"license": "MIT",
"dependencies": {
- "strnum": "^2.1.0"
+ "fast-xml-builder": "^1.0.0",
+ "strnum": "^2.1.2"
},
"bin": {
"fxparser": "src/cli/cli.js"
@@ -6057,9 +6052,9 @@
}
},
"node_modules/form-data": {
- "version": "4.0.4",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
- "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
+ "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
@@ -6288,16 +6283,37 @@
"node": ">= 6"
}
},
+ "node_modules/glob/node_modules/balanced-match": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+ "license": "MIT",
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/glob/node_modules/brace-expansion": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz",
+ "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==",
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^4.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
"node_modules/glob/node_modules/minimatch": {
- "version": "10.1.1",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz",
- "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==",
+ "version": "10.2.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
+ "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==",
"license": "BlueOak-1.0.0",
"dependencies": {
- "@isaacs/brace-expansion": "^5.0.0"
+ "brace-expansion": "^5.0.2"
},
"engines": {
- "node": "20 || >=22"
+ "node": "18 || 20 || >=22"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
@@ -7777,12 +7793,12 @@
}
},
"node_modules/minimatch": {
- "version": "9.0.5",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
- "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "version": "9.0.9",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
+ "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
"license": "ISC",
"dependencies": {
- "brace-expansion": "^2.0.1"
+ "brace-expansion": "^2.0.2"
},
"engines": {
"node": ">=16 || 14 >=14.17"
@@ -7795,6 +7811,7 @@
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -7836,21 +7853,22 @@
"license": "MIT"
},
"node_modules/multer": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.2.tgz",
- "integrity": "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==",
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/multer/-/multer-2.1.1.tgz",
+ "integrity": "sha512-mo+QTzKlx8R7E5ylSXxWzGoXoZbOsRMpyitcht8By2KHvMbf3tjwosZ/Mu/XYU6UuJ3VZnODIrak5ZrPiPyB6A==",
"license": "MIT",
"dependencies": {
"append-field": "^1.0.0",
"busboy": "^1.6.0",
"concat-stream": "^2.0.0",
- "mkdirp": "^0.5.6",
- "object-assign": "^4.1.1",
- "type-is": "^1.6.18",
- "xtend": "^4.0.2"
+ "type-is": "^1.6.18"
},
"engines": {
"node": ">= 10.16.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
}
},
"node_modules/multer/node_modules/media-typer": {
@@ -7883,18 +7901,6 @@
"node": ">= 0.6"
}
},
- "node_modules/multer/node_modules/mkdirp": {
- "version": "0.5.6",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
- "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
- "license": "MIT",
- "dependencies": {
- "minimist": "^1.2.6"
- },
- "bin": {
- "mkdirp": "bin/cmd.js"
- }
- },
"node_modules/multer/node_modules/type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
@@ -7990,6 +7996,29 @@
}
}
},
+ "node_modules/node-pg-migrate/node_modules/balanced-match": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/node-pg-migrate/node_modules/brace-expansion": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz",
+ "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^4.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
"node_modules/node-pg-migrate/node_modules/glob": {
"version": "11.0.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz",
@@ -8015,16 +8044,16 @@
}
},
"node_modules/node-pg-migrate/node_modules/minimatch": {
- "version": "10.1.1",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz",
- "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==",
+ "version": "10.2.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
+ "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
- "@isaacs/brace-expansion": "^5.0.0"
+ "brace-expansion": "^5.0.2"
},
"engines": {
- "node": "20 || >=22"
+ "node": "18 || 20 || >=22"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
@@ -8085,9 +8114,9 @@
}
},
"node_modules/nodemon/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
"license": "ISC",
"dependencies": {
"brace-expansion": "^1.1.7"
@@ -8286,6 +8315,21 @@
"node": ">=8"
}
},
+ "node_modules/path-expression-matcher": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.1.2.tgz",
+ "integrity": "sha512-LXWqJmcpp2BKOEmgt4CyuESFmBfPuhJlAHKJsFzuJU6CxErWk75BrO+Ni77M9OxHN6dCYKM4vj+21Z6cOL96YQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
"node_modules/path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
@@ -8658,9 +8702,9 @@
"license": "MIT"
},
"node_modules/qs": {
- "version": "6.14.1",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
- "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==",
+ "version": "6.14.2",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz",
+ "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==",
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.1.0"
@@ -8757,9 +8801,9 @@
}
},
"node_modules/readdir-glob/node_modules/minimatch": {
- "version": "5.1.6",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
- "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+ "version": "5.1.9",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz",
+ "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -9413,9 +9457,9 @@
}
},
"node_modules/strnum": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.2.tgz",
- "integrity": "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==",
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.0.tgz",
+ "integrity": "sha512-Y7Bj8XyJxnPAORMZj/xltsfo55uOiyHcU2tnAVzHUnSJR/KsEX+9RoDeXEnsXtl/CX4fAcrt64gZ13aGaWPeBg==",
"funding": [
{
"type": "github",
@@ -9576,9 +9620,9 @@
}
},
"node_modules/test-exclude/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
"dev": true,
"license": "ISC",
"dependencies": {
diff --git a/backend/package.json b/backend/package.json
index 61b0909..7980648 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -1,6 +1,6 @@
{
"name": "support_backend",
- "version": "1.4.1",
+ "version": "1.4.4",
"appMeta": {
"db_version": "1.6.0",
"docker_image": "yqni13/support",
diff --git a/backend/src/configs/config.ts b/backend/src/configs/config.ts
index 528e618..e21a57b 100644
--- a/backend/src/configs/config.ts
+++ b/backend/src/configs/config.ts
@@ -1,12 +1,10 @@
export const Config: any = {
- ADMIN_API: process.env.SECRET_ADMIN_API || null,
+ ADMIN_KEY: process.env.SECRET_ADMIN_KEY || null,
ENV_MODE: process.env.NODE_ENV || 'development',
PORT: process.env.ENV_PORT || 3000,
EMAIL_RECEIVER: process.env.SECRET_EMAIL_RECEIVER || null,
EMAIL_SENDER: process.env.SECRET_EMAIL_SENDER || null,
EMAIL_PASS: process.env.SECRET_EMAIL_PASS || null,
- PRIVATE_KEY_ARTDV: process.env.SECRET_PRIVATE_KEY_ARTDV || null,
- PRIVATE_KEY_TAVA: process.env.SECRET_PRIVATE_KEY_TAVA || null,
BETTERSTACK_LOGGING_KEY: process.env.SECRET_BETTERSTACK_LOGGING_KEY || null,
BETTERSTACK_HOST: process.env.SECRET_BETTERSTACK_HOST || null,
DB_USER: process.env.SECRET_DB_USER || null,
@@ -14,19 +12,19 @@ export const Config: any = {
DB_HOST: process.env.SECRET_DB_HOST || null,
DB_PORT: process.env.SECRET_DB_PORT || null,
DB_DATABASE: process.env.SECRET_DB_DATABASE || null,
- DB_TEST_USER: process.env.SECRET_DB_TEST_USER || null,
- DB_TEST_PASS: process.env.SECRET_DB_TEST_PASS || null,
- DB_TEST_HOST: process.env.SECRET_DB_TEST_HOST || null,
- DB_TEST_PORT: process.env.SECRET_DB_TEST_PORT || null,
- DB_TEST_DATABASE: process.env.SECRET_DB_TEST_DATABASE || null,
+ DB_TEST_USER: process.env.SECRET_DB_TEST_USER || 'testuser',
+ DB_TEST_PASS: process.env.SECRET_DB_TEST_PASS || 'testpass',
+ DB_TEST_HOST: process.env.SECRET_DB_TEST_HOST || 'localhost',
+ DB_TEST_PORT: process.env.SECRET_DB_TEST_PORT || 5432,
+ DB_TEST_DATABASE: process.env.SECRET_DB_TEST_DATABASE || 'testdb',
TEST_APIKEY_RAW: process.env.SECRET_TEST_APIKEY_RAW || null,
TEST_APIKEY_HASH: process.env.SECRET_TEST_APIKEY_HASH || null,
- RATELIMITS_CLIENTSBURSTLIMIT: process.env.SECRET_RATELIMITS_CLIENTSBURSTLIMIT || null,
- RATELIMITS_CLIENTSDAILYLIMIT: process.env.SECRET_RATELIMITS_CLIENTSDAILYLIMIT || null,
- RATELIMITS_USERSBURSTLIMIT: process.env.SECRET_RATELIMITS_USERSBURSTLIMIT || null,
- RATELIMITS_USERSDAILYLIMIT: process.env.SECRET_RATELIMITS_USERSDAILYLIMIT || null,
- RATELIMITS_TOTALDAILYLIMIT: process.env.SECRET_RATELIMITS_TOTALDAILYLIMIT || null,
- DEMOLIMITS_TOTALDAILYLIMIT: process.env.SECRET_DEMOLIMITS_TOTALDAILYLIMIT || null,
+ RATELIMITS_CLIENTSBURSTLIMIT: process.env.SECRET_RATELIMITS_CLIENTSBURSTLIMIT || 99,
+ RATELIMITS_CLIENTSDAILYLIMIT: process.env.SECRET_RATELIMITS_CLIENTSDAILYLIMIT || 99,
+ RATELIMITS_USERSBURSTLIMIT: process.env.SECRET_RATELIMITS_USERSBURSTLIMIT || 99,
+ RATELIMITS_USERSDAILYLIMIT: process.env.SECRET_RATELIMITS_USERSDAILYLIMIT || 99,
+ RATELIMITS_TOTALDAILYLIMIT: process.env.SECRET_RATELIMITS_TOTALDAILYLIMIT || 99,
+ DEMOLIMITS_TOTALDAILYLIMIT: process.env.SECRET_DEMOLIMITS_TOTALDAILYLIMIT || 20,
CLOUD_BUCKET: process.env.SECRET_CLOUD_BUCKET || null,
CLOUD_ENDPOINT: process.env.SECRET_CLOUD_ENDPOINT || null,
CLOUD_ACCESS_KEY_ID: process.env.SECRET_CLOUD_ACCESS_KEY_ID || null,
diff --git a/backend/src/configs/db.ts b/backend/src/configs/db.ts
index 1449b69..499256b 100644
--- a/backend/src/configs/db.ts
+++ b/backend/src/configs/db.ts
@@ -16,7 +16,7 @@ export class DBConnection {
#pool: pg.Pool;
constructor() {
- const connectionString = this._getConnectionString(secrets.ENV_MODE);
+ const connectionString = this.getConnectionString(secrets.ENV_MODE);
this.#pool = new pg.Pool({connectionString});
}
@@ -27,7 +27,7 @@ export class DBConnection {
return DBConnection.instance;
}
- _getConnectionString(env: string) {
+ private getConnectionString(env: string) {
// Remove white spaces to be comparable with enum values.
env = env.trim() as EnvMode;
let db: string;
diff --git a/backend/src/controllers/clients.controller.ts b/backend/src/controllers/clients.controller.ts
index 11dd7c9..17be71e 100644
--- a/backend/src/controllers/clients.controller.ts
+++ b/backend/src/controllers/clients.controller.ts
@@ -7,6 +7,7 @@ import {
ClientsStatusUpdateDTO
} from "../dtos/clients.dto";
import clientsService from "../services/clients.service";
+import { ClientsId } from "../repositories/interfaces/clients.entity.interface";
class ClientsController {
async getClientStatus(req: Request, res: Response, next: NextFunction) {
@@ -34,7 +35,7 @@ class ClientsController {
async patchClientStatus(req: Request, res: Response, next: NextFunction) {
try {
checkValidation(req);
- const id: string = req.params.id;
+ const id = req.params.id as ClientsId;
const dto: ClientsStatusUpdateDTO = req.body;
const response: ClientsStatusResponseDTO | null = await clientsService.updateClientStatus(id, dto);
res.json(response);
diff --git a/backend/src/controllers/feedback-rating.controller.ts b/backend/src/controllers/feedback-rating.controller.ts
index ac199f8..8cb13b1 100644
--- a/backend/src/controllers/feedback-rating.controller.ts
+++ b/backend/src/controllers/feedback-rating.controller.ts
@@ -2,12 +2,13 @@ import { NextFunction, Request, Response } from "express";
import { checkValidation } from "../middleware/validation.middleware";
import feedbackRatingService from "../services/feedback-rating.service";
import { FeedbackRatingExtendedResponseDTO, FeedbackRatingResponseDTO } from "../dtos/feedback-rating.dto";
+import { ClientsId } from "../repositories/interfaces/clients.entity.interface";
class FeedbackRatingController {
async getExtendedFeedbackRating(req: Request, res: Response, next: NextFunction) {
try {
checkValidation(req);
- const id = req.params.id;
+ const id = req.params.id as ClientsId;
const response: FeedbackRatingExtendedResponseDTO | null = await feedbackRatingService.getExtendedFeedbackRatingById(id);
res.json(response);
} catch(err: any) {
diff --git a/backend/src/controllers/feedback.controller.ts b/backend/src/controllers/feedback.controller.ts
index f370374..a55595b 100644
--- a/backend/src/controllers/feedback.controller.ts
+++ b/backend/src/controllers/feedback.controller.ts
@@ -2,12 +2,13 @@ import { NextFunction, Request, Response } from "express";
import { checkValidation } from "../middleware/validation.middleware";
import { FeedbackCreateDTO, FeedbackFilterDTO, FeedbackResponseDTO } from "../dtos/feedback.dto";
import feedbackService from "../services/feedback.service";
+import { FeedbackId } from "../repositories/interfaces/feedback.entity.interface";
class FeedbackController {
async getFeedback(req: Request, res: Response, next: NextFunction) {
try {
checkValidation(req);
- const id = +req.params.id;
+ const id = (+req.params.id) as FeedbackId;
const response: FeedbackResponseDTO | null = await feedbackService.getFeedbackById(id);
res.json(response);
} catch(err: any) {
@@ -44,7 +45,7 @@ class FeedbackController {
async patchFeedbackReview(req: Request, res: Response, next: NextFunction) {
try {
checkValidation(req);
- const id = +req.params.id;
+ const id = (+req.params.id) as FeedbackId;
const response: FeedbackResponseDTO | null = await feedbackService.updateFeedbackReview(id);
res.json(response);
} catch(err: any) {
diff --git a/backend/src/controllers/meta.controller.ts b/backend/src/controllers/meta.controller.ts
index 6ee6b94..a44a984 100644
--- a/backend/src/controllers/meta.controller.ts
+++ b/backend/src/controllers/meta.controller.ts
@@ -2,12 +2,13 @@ import { NextFunction, Request, Response } from "express";
import metaService from "../services/meta.service";
import { checkValidation } from "../middleware/validation.middleware";
import { MaintenanceResponseDTO, MaintenanceUpdateDTO, MetaResponseDTO, MetaUpdateDTO } from "../dtos/meta.dto";
+import { MetaId } from "../repositories/interfaces/meta.entity.interface";
class MetaController {
async getMetaById(req: Request, res: Response, next: NextFunction) {
try {
checkValidation(req);
- const id: number = +(req.params.id);
+ const id = (+req.params.id) as MetaId;
const response: MetaResponseDTO | null = await metaService.getMetaById(id);
res.json(response);
} catch(err: any) {
@@ -38,7 +39,7 @@ class MetaController {
async patchMeta(req: Request, res: Response, next: NextFunction) {
try {
checkValidation(req);
- const id: number = +(req.params.id);
+ const id = (+req.params.id) as MetaId;
const dto: MetaUpdateDTO = req.body;
const response: MetaResponseDTO | null = await metaService.updateMeta(id, dto);
res.json(response);
@@ -61,7 +62,7 @@ class MetaController {
async patchMaintenanceMode(req: Request, res: Response, next: NextFunction) {
try {
checkValidation(req);
- const id: number = +req.params.id;
+ const id = (+req.params.id) as MetaId;
const dto: MaintenanceUpdateDTO = req.body;
const response: MaintenanceResponseDTO | null = await metaService.updateMaintenanceMode(id, dto);
res.json(response);
diff --git a/backend/src/controllers/tickets.controller.ts b/backend/src/controllers/tickets.controller.ts
index 602ff7e..8f34398 100644
--- a/backend/src/controllers/tickets.controller.ts
+++ b/backend/src/controllers/tickets.controller.ts
@@ -8,12 +8,13 @@ import {
TicketsUpdateDTO
} from "../dtos/tickets.dto";
import ticketsService from "../services/tickets.service";
+import { TicketsId } from "../repositories/interfaces/tickets.entity.interface";
class TicketsController {
async getTicket(req: Request, res: Response, next: NextFunction) {
try {
checkValidation(req);
- const id = req.params.id;
+ const id = req.params.id as TicketsId;
const response: TicketsResponseExtendedDTO | null = await ticketsService.getTicketById(id);
res.json(response);
} catch(err: any) {
@@ -65,7 +66,7 @@ class TicketsController {
async patchTicket(req: Request, res: Response, next: NextFunction) {
try {
checkValidation(req);
- const id: string = req.params.id;
+ const id = req.params.id as TicketsId;
const dto: TicketsUpdateDTO = req.body;
const response: TicketsResponseDTO | null = await ticketsService.updateTicket(id, dto);
res.json(response);
@@ -77,7 +78,7 @@ class TicketsController {
async deleteTicket(req: Request, res: Response, next: NextFunction) {
try {
checkValidation(req);
- const id = req.params.id;
+ const id = req.params.id as TicketsId;
const response: boolean = await ticketsService.deleteTicket(id);
res.json(response);
} catch(err: any) {
diff --git a/backend/src/controllers/users.controller.ts b/backend/src/controllers/users.controller.ts
index 23d9dde..9902242 100644
--- a/backend/src/controllers/users.controller.ts
+++ b/backend/src/controllers/users.controller.ts
@@ -2,12 +2,13 @@ import { NextFunction, Request, Response } from "express";
import { UsersUpdateDTO, UsersFilterDTO, UsersResponseDTO, UsersCreateDTO } from "../dtos/users.dto";
import { checkValidation } from "../middleware/validation.middleware";
import usersService from "../services/users.service";
+import { UsersId } from "../repositories/interfaces/users.entity.interface";
class UsersController {
async getUserById(req: Request, res: Response, next: NextFunction) {
try {
checkValidation(req);
- const id = req.params.id;
+ const id = req.params.id as UsersId;
const response: UsersResponseDTO | null = await usersService.getUserById(id);
res.json(response);
} catch(err: any) {
@@ -65,7 +66,7 @@ class UsersController {
async patchUser(req: Request, res: Response, next: NextFunction) {
try {
checkValidation(req);
- const id: string = req.params.id;
+ const id = req.params.id as UsersId;
const dto: UsersUpdateDTO = req.body;
const response: UsersResponseDTO | null = await usersService.updateUser(id, dto);
res.json(response);
diff --git a/backend/src/dtos/clients.dto.ts b/backend/src/dtos/clients.dto.ts
index e72ce5c..ab95c0a 100644
--- a/backend/src/dtos/clients.dto.ts
+++ b/backend/src/dtos/clients.dto.ts
@@ -1,3 +1,4 @@
+import { ClientsId } from "../repositories/interfaces/clients.entity.interface";
import { ApiKeyStatus } from "../utils/enums/api-key-status.enum";
import { Flag } from "../utils/enums/flag.enum";
@@ -20,7 +21,7 @@ export interface ClientsLastUseUpdateDTO {
}
export interface ClientsExistResponseDTO {
- client_id: string,
+ client_id: ClientsId,
name: string,
api_key_hash: string,
status: ApiKeyStatus,
@@ -31,7 +32,7 @@ export interface ClientsExistResponseDTO {
}
export interface ClientsCreateResponseDTO {
- client_id: string,
+ client_id: ClientsId,
name: string,
api_key: string,
status: ApiKeyStatus,
@@ -42,7 +43,7 @@ export interface ClientsCreateResponseDTO {
}
export interface ClientsFlagResponseDTO {
- client_id: string,
+ client_id: ClientsId,
flag: Flag | null,
last_use: string,
last_modified: string,
@@ -50,7 +51,7 @@ export interface ClientsFlagResponseDTO {
}
export interface ClientsStatusResponseDTO {
- client_id: string,
+ client_id: ClientsId,
name: string,
status: ApiKeyStatus,
last_use: string,
@@ -59,7 +60,7 @@ export interface ClientsStatusResponseDTO {
}
export interface ClientsLastUseResponseDTO {
- client_id: string,
+ client_id: ClientsId,
name: string,
last_use: string,
last_modified: string,
diff --git a/backend/src/dtos/demo-limits.dto.ts b/backend/src/dtos/demo-limits.dto.ts
index 429a291..7f458b4 100644
--- a/backend/src/dtos/demo-limits.dto.ts
+++ b/backend/src/dtos/demo-limits.dto.ts
@@ -1,3 +1,5 @@
+import { DemoLimitsId } from "../repositories/interfaces/demo-limits.entity.interface"
+
export interface DemoLimitsCountDTO {
day: string
}
@@ -8,7 +10,7 @@ export interface DemoLimitsUpdateDTO {
}
export interface DemoLimitsResponseDTO {
- demo_limit_id: number,
+ demo_limit_id: DemoLimitsId,
day: string,
count: number,
last_modified: string
diff --git a/backend/src/dtos/feedback-rating.dto.ts b/backend/src/dtos/feedback-rating.dto.ts
index 4408a4a..b9e638b 100644
--- a/backend/src/dtos/feedback-rating.dto.ts
+++ b/backend/src/dtos/feedback-rating.dto.ts
@@ -1,5 +1,7 @@
+import { ClientsId } from "../repositories/interfaces/clients.entity.interface"
+
export interface FeedbackRatingCreateDTO {
- client_id: string,
+ client_id: ClientsId,
count?: number,
rating_sum?: number
}
@@ -15,7 +17,7 @@ export interface FeedbackRatingResponseDTO {
}
export interface FeedbackRatingExtendedResponseDTO extends FeedbackRatingResponseDTO {
- client_id: string,
+ client_id: ClientsId,
count: number,
rating_sum: number,
last_modified: string,
diff --git a/backend/src/dtos/feedback.dto.ts b/backend/src/dtos/feedback.dto.ts
index 73e26eb..e042d8c 100644
--- a/backend/src/dtos/feedback.dto.ts
+++ b/backend/src/dtos/feedback.dto.ts
@@ -1,6 +1,10 @@
+import { ClientsId } from "../repositories/interfaces/clients.entity.interface"
+import { FeedbackId } from "../repositories/interfaces/feedback.entity.interface"
+import { UsersId } from "../repositories/interfaces/users.entity.interface"
+
export interface FeedbackCreateDTO {
- client_id: string,
- user_id: string,
+ client_id: ClientsId,
+ user_id: UsersId,
rating: number,
term_accepted: boolean,
message?: string,
@@ -19,8 +23,8 @@ export interface FeedbackUpdateReviewDTO {
}
export interface FeedbackFilterDTO {
- client_id?: string | string[],
- user_id?: string | string[],
+ client_id?: ClientsId | ClientsId[],
+ user_id?: UsersId | UsersId[],
rating?: number | number[],
term_accepted?: boolean,
reviewed_on?: string | string[],
@@ -29,9 +33,9 @@ export interface FeedbackFilterDTO {
}
export interface FeedbackResponseDTO {
- feedback_id: number,
- client_id: string,
- user_id: string,
+ feedback_id: FeedbackId,
+ client_id: ClientsId,
+ user_id: UsersId,
rating: number,
rating_average_new?: number,
rating_old?: number,
@@ -39,5 +43,6 @@ export interface FeedbackResponseDTO {
message?: string,
reviewed_on?: string,
last_modified: string,
- created_on: string
+ created_on: string,
+ blocked?: boolean
}
diff --git a/backend/src/dtos/meta.dto.ts b/backend/src/dtos/meta.dto.ts
index 4fc3b44..3216424 100644
--- a/backend/src/dtos/meta.dto.ts
+++ b/backend/src/dtos/meta.dto.ts
@@ -1,3 +1,4 @@
+import { MetaId } from "../repositories/interfaces/meta.entity.interface"
import { EnvMode } from "../utils/enums/env-mode.enum"
import { MaintenanceMode } from "../utils/enums/maintenance-mode.enum"
@@ -15,7 +16,7 @@ export interface MetaUpdateDTO {
}
export interface MetaResponseDTO {
- id: number,
+ id: MetaId,
app: string,
author: string,
build_on: string,
@@ -36,7 +37,7 @@ export interface MaintenanceUpdateDTO {
}
export interface MaintenanceResponseDTO {
- id: number,
+ id: MetaId,
app: string,
build_on: string,
maintenance_mode: MaintenanceMode,
diff --git a/backend/src/dtos/rate-limits.dto.ts b/backend/src/dtos/rate-limits.dto.ts
index bf6fb39..e6cc154 100644
--- a/backend/src/dtos/rate-limits.dto.ts
+++ b/backend/src/dtos/rate-limits.dto.ts
@@ -1,25 +1,29 @@
+import { ClientsId } from "../repositories/interfaces/clients.entity.interface"
+import { RateLimitsId } from "../repositories/interfaces/rate-limits.entity.interface"
+import { UsersId } from "../repositories/interfaces/users.entity.interface"
+
export interface RateLimitsCountDTO {
- client_id?: string,
- user_id?: string,
+ client_id?: ClientsId,
+ user_id?: UsersId,
day: string
}
export interface RateLimitsCreateDTO {
- client_id: string,
- user_id: string
+ client_id: ClientsId,
+ user_id: UsersId
}
export interface RateLimitsUpdateDTO {
- client_id: string,
- user_id: string,
+ client_id: ClientsId,
+ user_id: UsersId,
day?: string,
last_modified?: string
}
export interface RateLimitsResponseDTO {
- rate_limit_id: number,
- client_id: string,
- user_id: string,
+ rate_limit_id: RateLimitsId,
+ client_id: ClientsId,
+ user_id: UsersId,
day: string,
count: number,
last_modified: string
diff --git a/backend/src/dtos/tickets.dto.ts b/backend/src/dtos/tickets.dto.ts
index 79fd8db..d894b47 100644
--- a/backend/src/dtos/tickets.dto.ts
+++ b/backend/src/dtos/tickets.dto.ts
@@ -1,18 +1,20 @@
-import { SingleOrArray } from "../utils/custom-types.utils";
+import { ClientsId } from "../repositories/interfaces/clients.entity.interface";
+import { TicketsId } from "../repositories/interfaces/tickets.entity.interface";
+import { UsersId } from "../repositories/interfaces/users.entity.interface";
import { DeviceOption } from "../utils/enums/device-option.enum";
import { Flag } from "../utils/enums/flag.enum";
import { TicketOption } from "../utils/enums/ticket-option.enum";
import { TicketStatus } from "../utils/enums/ticket-status.enum";
export interface TicketsIntervalDTO {
- client_id?: string,
- user_id?: string,
+ client_id?: ClientsId,
+ user_id?: UsersId,
intervalTime: string
}
export interface TicketsCreateDTO {
- client_id: string,
- user_id: string,
+ client_id: ClientsId,
+ user_id: UsersId,
option: TicketOption,
title: string,
message: string,
@@ -49,20 +51,20 @@ export interface TicketsUpdateDTO {
}
export interface TicketsFilterDTO {
- client_id?: SingleOrArray
,
- user_id?: SingleOrArray,
+ client_id?: ClientsId | ClientsId[],
+ user_id?: UsersId | UsersId[],
title?: string,
- status?: SingleOrArray,
- option?: SingleOrArray,
- flag?: SingleOrArray | null,
+ status?: TicketStatus | TicketStatus[],
+ option?: TicketOption | TicketOption[],
+ flag?: Flag | Flag[] | null,
last_modified?: string[],
created_on?: string[]
}
export interface TicketsResponseDTO {
- ticket_id: string,
- client_id: string,
- user_id: string,
+ ticket_id: TicketsId,
+ client_id: ClientsId,
+ user_id: UsersId,
status: TicketStatus,
option: TicketOption,
title: string,
@@ -77,10 +79,10 @@ export interface TicketsResponseDTO {
}
export interface TicketsResponseExtendedDTO {
- ticket_id: string,
- client_id: string,
+ ticket_id: TicketsId,
+ client_id: ClientsId,
client_name: string,
- user_id: string,
+ user_id: UsersId,
user_email: string,
status: TicketStatus,
option: TicketOption,
diff --git a/backend/src/dtos/users.dto.ts b/backend/src/dtos/users.dto.ts
index 0c14b92..67ff9c6 100644
--- a/backend/src/dtos/users.dto.ts
+++ b/backend/src/dtos/users.dto.ts
@@ -1,6 +1,6 @@
import { UserStatus } from "../utils/enums/user-status.enum";
import { Flag } from "../utils/enums/flag.enum";
-import { SingleOrArray } from "../utils/custom-types.utils";
+import { UsersId } from "../repositories/interfaces/users.entity.interface";
export interface UsersCreateDTO {
email: string,
@@ -19,15 +19,15 @@ export interface UsersFlagUpdateDTO {
}
export interface UsersFilterDTO {
- email?: SingleOrArray,
- status?: SingleOrArray,
- flag?: SingleOrArray | null,
+ email?: string | string[],
+ status?: UserStatus | UserStatus[],
+ flag?: Flag | Flag[] | null,
last_modified?: string[],
created_on?: string[]
}
export interface UsersResponseDTO {
- user_id: string,
+ user_id: UsersId,
email: string,
status: UserStatus,
flag: Flag | null,
diff --git a/backend/src/middleware/auth.admin.middleware.ts b/backend/src/middleware/auth.admin.middleware.ts
index 3dd93b8..b20de12 100644
--- a/backend/src/middleware/auth.admin.middleware.ts
+++ b/backend/src/middleware/auth.admin.middleware.ts
@@ -17,7 +17,7 @@ export function authAdmin() {
throw new MissingApiKeyException('support-missing-admin-auth');
}
- const hasValidKey = adminKey.trim() === secrets.ADMIN_API.trim();
+ const hasValidKey = adminKey.trim() === secrets.ADMIN_KEY.trim();
if(!hasValidKey) {
throw new InvalidApiKeyException('support-invalid-admin-auth');
}
diff --git a/backend/src/middleware/container/penalty.container.middleware.ts b/backend/src/middleware/container/penalty.container.middleware.ts
index 036371d..b0ee529 100644
--- a/backend/src/middleware/container/penalty.container.middleware.ts
+++ b/backend/src/middleware/container/penalty.container.middleware.ts
@@ -11,6 +11,6 @@ export const penaltyHandler = new PenaltyHandler(
new Map([
[Violation.CLIENTSFLAG, new ClientsFlagPenalty()],
[Violation.USERSFLAG, new UsersFlagPenalty()],
- [Violation.MAINTENANCE_TRAFFIC, new MaintenanceTrafficPenalty]
+ [Violation.MAINTENANCE_TRAFFIC, new MaintenanceTrafficPenalty()]
])
)
\ No newline at end of file
diff --git a/backend/src/middleware/interfaces/penalties.interface.middleware.ts b/backend/src/middleware/interfaces/penalties.interface.middleware.ts
index 76a1395..513b3f7 100644
--- a/backend/src/middleware/interfaces/penalties.interface.middleware.ts
+++ b/backend/src/middleware/interfaces/penalties.interface.middleware.ts
@@ -1,3 +1,6 @@
+import { ClientsId } from "../../repositories/interfaces/clients.entity.interface";
+import { MetaId } from "../../repositories/interfaces/meta.entity.interface";
+import { UsersId } from "../../repositories/interfaces/users.entity.interface";
import { Flag } from "../../utils/enums/flag.enum";
import { MaintenanceMode } from "../../utils/enums/maintenance-mode.enum";
import { Violation } from "../../utils/enums/violations.enum";
@@ -14,24 +17,24 @@ export interface PenaltyApply {
}
export type PenaltyContext =
- | { type: Violation.CLIENTSFLAG, id: string, penaltyValue: Flag | null }
- | { type: Violation.USERSFLAG, id: string, penaltyValue: Flag | null }
- | { type: Violation.MAINTENANCE_TRAFFIC, id: number, penaltyValue: MaintenanceMode };
+ | { type: Violation.CLIENTSFLAG, id: ClientsId, penaltyValue: Flag | null }
+ | { type: Violation.USERSFLAG, id: UsersId, penaltyValue: Flag | null }
+ | { type: Violation.MAINTENANCE_TRAFFIC, id: MetaId, penaltyValue: MaintenanceMode };
export interface PenaltyClientsFlagContext extends BasePenaltyContext {
type: Violation.CLIENTSFLAG,
- id: string,
+ id: ClientsId,
penaltyValue: Flag | null
}
export interface PenaltyUsersFlagContext extends BasePenaltyContext {
type: Violation.USERSFLAG,
- id: string,
+ id: UsersId,
penaltyValue: Flag | null
}
export interface PenaltyMaintenanceTrafficContext extends BasePenaltyContext {
type: Violation.MAINTENANCE_TRAFFIC,
- id: number,
+ id: MetaId,
penaltyValue: MaintenanceMode
}
\ No newline at end of file
diff --git a/backend/src/middleware/interfaces/rate-limits.interface.middleware.ts b/backend/src/middleware/interfaces/rate-limits.interface.middleware.ts
index 704a202..11caa32 100644
--- a/backend/src/middleware/interfaces/rate-limits.interface.middleware.ts
+++ b/backend/src/middleware/interfaces/rate-limits.interface.middleware.ts
@@ -1,10 +1,10 @@
-import { Clients } from "../../repositories/interfaces/clients.entity.interface";
-import { Users } from "../../repositories/interfaces/users.entity.interface";
+import { Clients, ClientsId } from "../../repositories/interfaces/clients.entity.interface";
+import { Users, UsersId } from "../../repositories/interfaces/users.entity.interface";
import { PenaltyContext } from "./penalties.interface.middleware";
export interface RateLimitsData {
- client_id: string,
- user_id: string,
+ client_id: ClientsId,
+ user_id: UsersId,
client?: Clients,
user?: Users,
}
diff --git a/backend/src/middleware/observe.middleware.ts b/backend/src/middleware/observe.middleware.ts
index b7e2ee1..6e056ba 100644
--- a/backend/src/middleware/observe.middleware.ts
+++ b/backend/src/middleware/observe.middleware.ts
@@ -14,6 +14,8 @@ import { RateLimitsData, RateLimitsResponse } from "./interfaces/rate-limits.int
import { ExceedMaxEndpointException } from "../utils/exceptions/api.exception";
import { DemoLimitsIncrement, RateLimitsIncrement } from "./adapter/rate-limits.adapter.middleware";
import { penaltyHandler } from "./container/penalty.container.middleware";
+import { ClientsId } from "../repositories/interfaces/clients.entity.interface";
+import { UsersId } from "../repositories/interfaces/users.entity.interface";
export function observe(isDemo: boolean = false) {
return async function (req: Request, res: Response, next: NextFunction) {
@@ -63,5 +65,5 @@ async function checkDemoLimits(): Promise {
],
new DemoLimitsIncrement()
);
- return await engine.process({ client_id: 'demo', user_id: 'demo'});
+ return await engine.process({ client_id: 'demo' as ClientsId, user_id: 'demo' as UsersId});
}
\ No newline at end of file
diff --git a/backend/src/middleware/rules/rate-limits.rule.middleware.ts b/backend/src/middleware/rules/rate-limits.rule.middleware.ts
index 15fac2a..2c09866 100644
--- a/backend/src/middleware/rules/rate-limits.rule.middleware.ts
+++ b/backend/src/middleware/rules/rate-limits.rule.middleware.ts
@@ -1,6 +1,7 @@
import { DemoLimitsCountDTO } from "../../dtos/demo-limits.dto";
import { RateLimitsCountDTO } from "../../dtos/rate-limits.dto";
import { TicketsIntervalDTO } from "../../dtos/tickets.dto";
+import { MetaId } from "../../repositories/interfaces/meta.entity.interface";
import demoLimitsService from "../../services/demo-limits.service";
import rateLimitsService from "../../services/rate-limits.service";
import ticketsService from "../../services/tickets.service";
@@ -108,7 +109,7 @@ export class TotalDailyLimitRule implements RateLimitsRule {
retryAfter: CommonUtils.getNextDayUTC(),
penalty: {
type: Violation.MAINTENANCE_TRAFFIC,
- id: 1,
+ id: 1 as MetaId,
penaltyValue: MaintenanceMode.T011
}
};
diff --git a/backend/src/models/clients.model.ts b/backend/src/models/clients.model.ts
index ecf980a..4c74f10 100644
--- a/backend/src/models/clients.model.ts
+++ b/backend/src/models/clients.model.ts
@@ -2,7 +2,7 @@ import {
ClientsCreateDTO,
ClientsCreateResponseDTO,
} from "../dtos/clients.dto";
-import { Clients } from "../repositories/interfaces/clients.entity.interface";
+import { Clients, ClientsId } from "../repositories/interfaces/clients.entity.interface";
import crypto from 'crypto';
import * as CommonUtils from "../utils/common.utils";
import { ApiKeyStatus } from "../utils/enums/api-key-status.enum";
@@ -14,7 +14,7 @@ class ClientsModel {
this.timeMapTargets = ['last_use', 'last_modified', 'created_on'];
}
- mapToCreateResponseDTO(data: Clients, apiKey: string): ClientsCreateResponseDTO {
+ toClientsCreateResponseDTO(data: Clients, apiKey: string): ClientsCreateResponseDTO {
data = CommonUtils.mapObjTimestamps(data, this.timeMapTargets);
return {
client_id: data.client_id,
@@ -28,7 +28,7 @@ class ClientsModel {
};
}
- _generateApiKeyObj(): { keyRaw: string, keyHash: string } {
+ private generateApiKeyObj(): { keyRaw: string, keyHash: string } {
const keyLength = 42;
const charset = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
const bytes = crypto.randomBytes(keyLength);
@@ -43,8 +43,8 @@ class ClientsModel {
}
generateClientsCreateObj(dto: ClientsCreateDTO): { client: Clients, keyRaw: string } {
- const id = CommonUtils.generateUUID();
- const keyObj = this._generateApiKeyObj();
+ const id = CommonUtils.generateUUID();
+ const keyObj = this.generateApiKeyObj();
const timestamp = CommonUtils.getTimestampUTC();
const client: Clients = {
client_id: id,
diff --git a/backend/src/models/test.model.ts b/backend/src/models/test.model.ts
index 35fab18..d8dfe50 100644
--- a/backend/src/models/test.model.ts
+++ b/backend/src/models/test.model.ts
@@ -3,7 +3,7 @@ import { MaintenanceMode } from "../utils/enums/maintenance-mode.enum";
import { ExceedMaxEndpointException, UnexpectedApiResponseException, UnimplementedException } from "../utils/exceptions/api.exception";
import { AuthSecretNotFoundException, BlockedUsersException, ForbiddenApiKeyException, InvalidApiKeyException, InvalidUsersException, MalformedApiKeyException, MissingApiKeyException, PermissionException } from "../utils/exceptions/auth.exception";
import { InternalServerException, InvalidSourceException, MaintenanceException, RequestExceedMaxException } from "../utils/exceptions/common.exception";
-import { DBConnectionException, DBEmptyException, DBQueryErrorException } from "../utils/exceptions/db.exception";
+import { DBConnectionException, DBConstraintErrorException, DBEmptyException, DBQueryErrorException } from "../utils/exceptions/db.exception";
import { InvalidFilesException, InvalidPropertiesException } from "../utils/exceptions/validation.exception";
class TestModel {
@@ -44,6 +44,8 @@ class TestModel {
throw new DBEmptyException();
case('DBQueryErrorException'):
throw new DBQueryErrorException();
+ case('DBConstraintErrorException'):
+ throw new DBConstraintErrorException(dto.errorMsg ?? this.substitutionMsg);
// ValidationException
case('InvalidPropertiesException'): {
const customError = [
diff --git a/backend/src/models/tickets.model.ts b/backend/src/models/tickets.model.ts
index 2a908f9..2b011ee 100644
--- a/backend/src/models/tickets.model.ts
+++ b/backend/src/models/tickets.model.ts
@@ -1,14 +1,14 @@
import { TicketsCreateDTO, TicketsResponseDTO, TicketsUpdateDTO } from "../dtos/tickets.dto";
-import { Tickets } from "../repositories/interfaces/tickets.entity.interface";
+import { Tickets, TicketsId } from "../repositories/interfaces/tickets.entity.interface";
import * as CommonUtils from "../utils/common.utils";
import { TicketStatus } from "../utils/enums/ticket-status.enum";
import { FilesService } from "../services/files.service";
import { PermissionException } from "../utils/exceptions/auth.exception";
class TicketsModel {
- async generateTicket(dto: TicketsCreateDTO, files: Express.Multer.File[] | null): Promise {
+ async generateTicketEntity(dto: TicketsCreateDTO, files: Express.Multer.File[] | null): Promise {
const timestamp = CommonUtils.getTimestampUTC();
- const newId = CommonUtils.generateUUID();
+ const newId = CommonUtils.generateUUID();
let paths: string[] | null = null;
if(files) {
const filesService = new FilesService(files, 'tickets');
diff --git a/backend/src/models/users.model.ts b/backend/src/models/users.model.ts
index ab34214..849b2d1 100644
--- a/backend/src/models/users.model.ts
+++ b/backend/src/models/users.model.ts
@@ -1,13 +1,13 @@
import { UsersCreateDTO } from "../dtos/users.dto";
-import { Users } from "../repositories/interfaces/users.entity.interface";
+import { Users, UsersId } from "../repositories/interfaces/users.entity.interface";
import * as CommonUtils from "../utils/common.utils";
import { UserStatus } from "../utils/enums/user-status.enum";
class UsersModel {
- generateUser(dto: UsersCreateDTO): Users {
+ generateUserEntity(dto: UsersCreateDTO): Users {
const timestamp = CommonUtils.getTimestampUTC();
return {
- user_id: CommonUtils.generateUUID(),
+ user_id: CommonUtils.generateUUID(),
email: dto.email,
status: UserStatus.ACTIVE,
flag: null,
diff --git a/backend/src/repositories/clients.repository.ts b/backend/src/repositories/clients.repository.ts
index 26adc3e..6e85de4 100644
--- a/backend/src/repositories/clients.repository.ts
+++ b/backend/src/repositories/clients.repository.ts
@@ -1,4 +1,4 @@
-import { Clients } from "./interfaces/clients.entity.interface";
+import { Clients, ClientsId } from "./interfaces/clients.entity.interface";
import { DBConnection } from "../configs/db";
import { QueryResult } from "pg";
import { ApiKeyStatus } from "../utils/enums/api-key-status.enum";
@@ -13,7 +13,7 @@ class ClientsRepository implements ICreateRepository, IUpdateFlagReposi
this.table = "clients";
}
- async findById(id: string): Promise {
+ async findById(id: ClientsId): Promise {
const filterColumn = 'client_id';
const sql = `SELECT * FROM ${this.table} WHERE ${filterColumn} = $1;`;
const value = [id];
@@ -94,7 +94,7 @@ class ClientsRepository implements ICreateRepository, IUpdateFlagReposi
}
}
- async updateFlag(id: string, dto: Partial): Promise {
+ async updateFlag(id: ClientsId, dto: Partial): Promise {
const filterColumn = "client_id";
const sql = `UPDATE ${this.table}
SET flag = $1, last_modified = $2
@@ -118,7 +118,7 @@ class ClientsRepository implements ICreateRepository, IUpdateFlagReposi
}
}
- async updateStatus(id: string, dto: Partial): Promise {
+ async updateStatus(id: ClientsId, dto: Partial): Promise {
const filterColumn = "client_id";
const sql = `UPDATE ${this.table}
SET status = $1, last_modified = $2
@@ -142,7 +142,7 @@ class ClientsRepository implements ICreateRepository, IUpdateFlagReposi
}
}
- async updateLastUse(id: string, dto: Partial): Promise {
+ async updateLastUse(id: ClientsId, dto: Partial): Promise {
const filterColumn = "client_id";
const sql = `UPDATE ${this.table}
SET last_use = $1
diff --git a/backend/src/repositories/feedback-rating.repository.ts b/backend/src/repositories/feedback-rating.repository.ts
index 84e9a82..d0251f6 100644
--- a/backend/src/repositories/feedback-rating.repository.ts
+++ b/backend/src/repositories/feedback-rating.repository.ts
@@ -5,6 +5,7 @@ import { DBQueryErrorException } from "../utils/exceptions/db.exception";
import { IFindRepository } from "./interfaces/base.repository.interface";
import { FeedbackRating } from "./interfaces/feedback-rating.entity.interface";
import { FeedbackRatingUpdateDTO } from "../dtos/feedback-rating.dto";
+import { ClientsId } from "./interfaces/clients.entity.interface";
class FeedbackRatingRepository implements
IFindRepository {
@@ -14,7 +15,7 @@ IFindRepository {
this.table = 'feedback_ratings';
}
- async findById(id: string): Promise {
+ async findById(id: ClientsId): Promise {
const filterColumn = 'client_id';
const sql = `SELECT * FROM ${this.table} WHERE ${filterColumn} = $1;`;
const value = [id];
@@ -92,7 +93,7 @@ IFindRepository {
/**
* @description Repository function to call only when used within a transaction => needs PoolClient as param.
*/
- async updateInTa(client: PoolClient, id: string, dto: FeedbackRatingUpdateDTO): Promise {
+ async updateInTa(client: PoolClient, id: ClientsId, dto: FeedbackRatingUpdateDTO): Promise {
const filterColumn = 'client_id';
const sql = `UPDATE ${this.table}
SET count = count + $1, rating_sum = rating_sum + $2, last_modified = $3::timestamp
diff --git a/backend/src/repositories/feedback.repository.ts b/backend/src/repositories/feedback.repository.ts
index 4b23fc6..d218d03 100644
--- a/backend/src/repositories/feedback.repository.ts
+++ b/backend/src/repositories/feedback.repository.ts
@@ -1,7 +1,7 @@
import { PoolClient, QueryResult } from "pg";
import { DBConnection } from "../configs/db";
import { FeedbackFilterDTO, FeedbackResponseDTO, FeedbackUpdateReviewDTO } from "../dtos/feedback.dto";
-import { Feedback } from "./interfaces/feedback.entity.interface";
+import { Feedback, FeedbackId } from "./interfaces/feedback.entity.interface";
import { DBQueryErrorException } from "../utils/exceptions/db.exception";
import { logError } from "../utils/common.utils";
import { mapFilteredQueryValues } from "../utils/repository.utils";
@@ -13,7 +13,7 @@ class FeedbackRepository {
this.table = 'feedback_entries';
}
- async findById(id: number): Promise {
+ async findById(id: FeedbackId): Promise {
const filterColumn = "feedback_id";
const sql = `SELECT * FROM ${this.table} WHERE ${filterColumn} = $1;`;
const value = [id];
@@ -51,7 +51,7 @@ class FeedbackRepository {
}
}
- async updateReview(id: number, dto: FeedbackUpdateReviewDTO): Promise {
+ async updateReview(id: FeedbackId, dto: FeedbackUpdateReviewDTO): Promise {
const filterColumn = "feedback_id";
const sql = `UPDATE ${this.table}
SET reviewed_on = $1::timestamp, last_modified = $2::timestamp
@@ -76,9 +76,21 @@ class FeedbackRepository {
}
/**
+ * Upsert feedback within a transaction.
*
- * @description Update on insert conflict for existing client_id and user_id combined entry (unique constraint). Is called within transaction only => needs PoolClient as param.
- * @returns {FeedbackResponseDTO | null} Entity expanded by rating_old value for further processing.
+ * Uses a CTE-based pattern to handle the following cases:
+ * - UDPATE: Existing entry => updates and returns row
+ * - INSERT: Non existing entry (unique constraint for client_id & user_id combination) => creates new row
+ * - BLOCK: Existing entry with message but without review => update is prevented by WHERE NOT clause.
+ * Returns existing row with `blocked: true` to handle specific response.
+ *
+ * Notes:
+ * - UNION ALL: Combine results of multiple SELECT statements.
+ * - ON CONFLICT: PostgreSQL does NOT have UPSERT statement => supports INSERT...ON CONFLICT instead (or MERGE).
+ *
+ * @param {PoolClient} client Client used to connect database as this fn is called within a transaction.
+ * @param {Partial} entity All properties needed except 'feedback_id' due to serial type in database.
+ * @returns {FeedbackResponseDTO | null} FeedbackResponseDTO expands `Feedback` by 'rating_old' and 'blocked' value for further processing => `blocked: true` for prevented update or `NULL` if nothing was found (unexpected).
*/
async upsertInTa(client: PoolClient, entity: Partial): Promise {
const sql = `
@@ -86,21 +98,30 @@ class FeedbackRepository {
SELECT rating
FROM ${this.table}
WHERE client_id = $1 AND user_id = $2
+ ),
+ upsert AS (
+ INSERT INTO ${this.table}
+ (client_id, user_id, rating, term_accepted, message, reviewed_on, last_modified, created_on)
+ VALUES ($1, $2, $3, $4, $5, $6, $7::timestamp, $8::timestamp)
+ ON CONFLICT (client_id, user_id)
+ DO UPDATE SET
+ rating = EXCLUDED.rating,
+ term_accepted = EXCLUDED.term_accepted,
+ message = EXCLUDED.message,
+ reviewed_on = EXCLUDED.reviewed_on,
+ last_modified = EXCLUDED.last_modified
+ WHERE NOT (${this.table}.message IS NOT NULL AND ${this.table}.reviewed_on IS NULL)
+ RETURNING
+ ${this.table}.*,
+ (SELECT rating FROM pre_update_data) AS rating_old,
+ false AS blocked
)
- INSERT INTO ${this.table}
- (client_id, user_id, rating, term_accepted, message, reviewed_on, last_modified, created_on)
- VALUES ($1, $2, $3, $4, $5, $6, $7::timestamp, $8::timestamp)
- ON CONFLICT (client_id, user_id)
- DO UPDATE SET
- rating = EXCLUDED.rating,
- term_accepted = EXCLUDED.term_accepted,
- message = EXCLUDED.message,
- reviewed_on = EXCLUDED.reviewed_on,
- last_modified = EXCLUDED.last_modified
- WHERE NOT (${this.table}.message IS NOT NULL AND ${this.table}.reviewed_on IS NOT NULL)
- RETURNING
- ${this.table}.*,
- (SELECT rating FROM pre_update_data) AS rating_old;
+ SELECT * FROM upsert
+ UNION ALL
+ SELECT *, NULL AS rating_old, true AS blocked
+ FROM ${this.table}
+ WHERE client_id = $1 AND user_id = $2
+ AND NOT EXISTS (SELECT 1 FROM upsert)
`;
const values = [entity.client_id, entity.user_id, entity.rating, entity.term_accepted, entity.message, null, entity.last_modified, entity.created_on];
const result: QueryResult = await client.query(sql, values);
diff --git a/backend/src/repositories/interfaces/clients.entity.interface.ts b/backend/src/repositories/interfaces/clients.entity.interface.ts
index c2d1cd3..67c3d76 100644
--- a/backend/src/repositories/interfaces/clients.entity.interface.ts
+++ b/backend/src/repositories/interfaces/clients.entity.interface.ts
@@ -1,8 +1,10 @@
import { ApiKeyStatus } from "../../utils/enums/api-key-status.enum";
import { Flag } from "../../utils/enums/flag.enum";
+export type ClientsId = string & { readonly brand: unique symbol };
+
export interface Clients {
- client_id: string,
+ client_id: ClientsId,
name: string,
api_key_hash: string,
status: ApiKeyStatus,
diff --git a/backend/src/repositories/interfaces/demo-limits.entity.interface.ts b/backend/src/repositories/interfaces/demo-limits.entity.interface.ts
index 4569370..9d756d0 100644
--- a/backend/src/repositories/interfaces/demo-limits.entity.interface.ts
+++ b/backend/src/repositories/interfaces/demo-limits.entity.interface.ts
@@ -1,5 +1,7 @@
+export type DemoLimitsId = number & { readonly brand: unique symbol };
+
export interface DemoLimits {
- demo_limit_id: number,
+ demo_limit_id: DemoLimitsId,
day: string,
count: number,
last_modified: string
diff --git a/backend/src/repositories/interfaces/feedback-rating.entity.interface.ts b/backend/src/repositories/interfaces/feedback-rating.entity.interface.ts
index 0cffb89..aeee659 100644
--- a/backend/src/repositories/interfaces/feedback-rating.entity.interface.ts
+++ b/backend/src/repositories/interfaces/feedback-rating.entity.interface.ts
@@ -1,6 +1,8 @@
+import { ClientsId } from "./clients.entity.interface";
+
// Use Clients FK as PK => 1:1 relationship as client is unique for each feedback-rating.
export interface FeedbackRating {
- client_id: string,
+ client_id: ClientsId,
count: number,
rating_sum: number,
last_modified: string,
diff --git a/backend/src/repositories/interfaces/feedback.entity.interface.ts b/backend/src/repositories/interfaces/feedback.entity.interface.ts
index 33fe571..79909c8 100644
--- a/backend/src/repositories/interfaces/feedback.entity.interface.ts
+++ b/backend/src/repositories/interfaces/feedback.entity.interface.ts
@@ -1,7 +1,12 @@
+import { ClientsId } from "./clients.entity.interface";
+import { UsersId } from "./users.entity.interface";
+
+export type FeedbackId = number & { readonly brand: unique symbol };
+
export interface Feedback {
- feedback_id: number,
- client_id: string,
- user_id: string,
+ feedback_id: FeedbackId,
+ client_id: ClientsId,
+ user_id: UsersId,
rating: number,
term_accepted: boolean,
message?: string,
diff --git a/backend/src/repositories/interfaces/meta.entity.interface.ts b/backend/src/repositories/interfaces/meta.entity.interface.ts
index 51c56d4..0a5b7bc 100644
--- a/backend/src/repositories/interfaces/meta.entity.interface.ts
+++ b/backend/src/repositories/interfaces/meta.entity.interface.ts
@@ -1,8 +1,10 @@
import { EnvMode } from "../../utils/enums/env-mode.enum";
import { MaintenanceMode } from "../../utils/enums/maintenance-mode.enum";
+export type MetaId = number & { readonly brand: unique symbol };
+
export interface Meta {
- id: number,
+ id: MetaId,
app: string,
author: string,
build_on: string,
@@ -18,7 +20,7 @@ export interface Meta {
}
export interface Maintenance {
- id: number,
+ id: MetaId,
app: string,
build_on: string,
maintenance_mode: MaintenanceMode,
diff --git a/backend/src/repositories/interfaces/rate-limits.entity.interface.ts b/backend/src/repositories/interfaces/rate-limits.entity.interface.ts
index 66d2221..2260ade 100644
--- a/backend/src/repositories/interfaces/rate-limits.entity.interface.ts
+++ b/backend/src/repositories/interfaces/rate-limits.entity.interface.ts
@@ -1,7 +1,12 @@
+import { ClientsId } from "./clients.entity.interface";
+import { UsersId } from "./users.entity.interface";
+
+export type RateLimitsId = number & { readonly brand: unique symbol };
+
export interface RateLimits {
- rate_limit_id: number,
- client_id: string,
- user_id: string,
+ rate_limit_id: RateLimitsId,
+ client_id: ClientsId,
+ user_id: UsersId,
day: string,
count: number,
last_modified: string
diff --git a/backend/src/repositories/interfaces/tickets.entity.interface.ts b/backend/src/repositories/interfaces/tickets.entity.interface.ts
index 20c31c9..4a482fe 100644
--- a/backend/src/repositories/interfaces/tickets.entity.interface.ts
+++ b/backend/src/repositories/interfaces/tickets.entity.interface.ts
@@ -2,11 +2,15 @@ import { DeviceOption } from "../../utils/enums/device-option.enum";
import { Flag } from "../../utils/enums/flag.enum";
import { TicketOption } from "../../utils/enums/ticket-option.enum";
import { TicketStatus } from "../../utils/enums/ticket-status.enum";
+import { ClientsId } from "./clients.entity.interface";
+import { UsersId } from "./users.entity.interface";
+
+export type TicketsId = string & { readonly brand: unique symbol };
export interface Tickets {
- ticket_id: string,
- client_id: string,
- user_id: string,
+ ticket_id: TicketsId,
+ client_id: ClientsId,
+ user_id: UsersId,
status: TicketStatus,
option: TicketOption,
title: string,
diff --git a/backend/src/repositories/interfaces/users.entity.interface.ts b/backend/src/repositories/interfaces/users.entity.interface.ts
index c387478..80384f3 100644
--- a/backend/src/repositories/interfaces/users.entity.interface.ts
+++ b/backend/src/repositories/interfaces/users.entity.interface.ts
@@ -1,8 +1,10 @@
import { Flag } from "../../utils/enums/flag.enum";
import { UserStatus } from "../../utils/enums/user-status.enum";
+export type UsersId = string & { readonly brand: unique symbol };
+
export interface Users {
- user_id: string,
+ user_id: UsersId,
email: string,
status: UserStatus,
flag: Flag | null,
diff --git a/backend/src/repositories/meta.repository.ts b/backend/src/repositories/meta.repository.ts
index 0689e63..1d99bad 100644
--- a/backend/src/repositories/meta.repository.ts
+++ b/backend/src/repositories/meta.repository.ts
@@ -1,7 +1,7 @@
import { QueryResult } from "pg";
import { DBConnection } from "../configs/db";
import { IBaseRepository, IFindRepository } from "./interfaces/base.repository.interface";
-import { Maintenance, Meta } from "./interfaces/meta.entity.interface";
+import { Maintenance, Meta, MetaId } from "./interfaces/meta.entity.interface";
import { logError } from "../utils/common.utils";
import { DBQueryErrorException } from "../utils/exceptions/db.exception";
@@ -13,7 +13,7 @@ class MetaRepository implements IBaseRepository, IFindRepository {
this.table = "meta";
}
- async findById(id: number): Promise {
+ async findById(id: MetaId): Promise {
const filterColumn = "id";
const sql = `SELECT
id, app, author, build_on, environment, app_version, db_version, docker_image, docker_version, jenkins_version, maintenance_mode, last_modified, created_on
@@ -103,7 +103,7 @@ class MetaRepository implements IBaseRepository, IFindRepository {
}
}
- async update(id: number, dto: Partial): Promise {
+ async update(id: MetaId, dto: Partial): Promise {
const filterColumn = "id";
const sql = `UPDATE ${this.table}
SET app = $1, author = $2, build_on = $3, environment = $4, app_version = $5, db_version = $6,
@@ -129,7 +129,7 @@ class MetaRepository implements IBaseRepository, IFindRepository {
}
}
- async updateMaintenance(id: number, dto: Partial): Promise {
+ async updateMaintenance(id: MetaId, dto: Partial): Promise {
const filterColumn = "id";
const sql = `UPDATE ${this.table}
SET maintenance_mode = $1, last_modified = $2
diff --git a/backend/src/repositories/tickets.repository.ts b/backend/src/repositories/tickets.repository.ts
index 79046f3..d9a5a9d 100644
--- a/backend/src/repositories/tickets.repository.ts
+++ b/backend/src/repositories/tickets.repository.ts
@@ -6,7 +6,7 @@ import {
} from "./interfaces/base.repository.interface";
import { DBConnection } from "../configs/db";
import { QueryResult } from "pg";
-import { Tickets } from "./interfaces/tickets.entity.interface";
+import { Tickets, TicketsId } from "./interfaces/tickets.entity.interface";
import { TicketsFilterDTO, TicketsIntervalDTO, TicketsResponseExtendedDTO } from "../dtos/tickets.dto";
import { DBQueryErrorException } from "../utils/exceptions/db.exception";
import { logError, now } from "../utils/common.utils";
@@ -24,7 +24,7 @@ IDeleteRepository
this.table = "tickets";
}
- async findById(id: string): Promise {
+ async findById(id: TicketsId): Promise {
const filterColumn = "ticket_id";
const sql = `SELECT
${this.table}.*,
@@ -138,7 +138,7 @@ IDeleteRepository
}
}
- async update(id: string, dto: Partial): Promise {
+ async update(id: TicketsId, dto: Partial): Promise {
const filterColumn = "ticket_id";
const sql = `UPDATE ${this.table}
SET status = $1, title = $2, message = $3, option = $4, flag = $5, info_browser = $6, info_os = $7,
@@ -163,7 +163,7 @@ IDeleteRepository
}
}
- async delete(id: string): Promise {
+ async delete(id: TicketsId): Promise {
const filterColumn = "ticket_id";
const sql = `DELETE FROM ${this.table} WHERE ${filterColumn} = $1;`;
const value = [id];
diff --git a/backend/src/repositories/users.repository.ts b/backend/src/repositories/users.repository.ts
index 063a3cd..d579c99 100644
--- a/backend/src/repositories/users.repository.ts
+++ b/backend/src/repositories/users.repository.ts
@@ -1,7 +1,7 @@
import { DBConnection } from "../configs/db";
import { QueryResult } from "pg";
import { IBaseRepository, ICreateRepository, IFindRepository, IUpdateFlagRepository } from "./interfaces/base.repository.interface";
-import { Users } from "./interfaces/users.entity.interface";
+import { Users, UsersId } from "./interfaces/users.entity.interface";
import { logError } from "../utils/common.utils";
import { UsersFilterDTO } from "../dtos/users.dto";
import { DBQueryErrorException } from "../utils/exceptions/db.exception";
@@ -19,7 +19,7 @@ IUpdateFlagRepository
this.table = "users";
}
- async findById(id: string): Promise {
+ async findById(id: UsersId): Promise {
const filterColumn = "user_id";
const sql = `SELECT * FROM ${this.table} WHERE ${filterColumn} = $1;`;
const value = [id];
@@ -122,7 +122,7 @@ IUpdateFlagRepository
}
}
- async update(id: string, dto: Partial): Promise {
+ async update(id: UsersId, dto: Partial): Promise {
const filterColumn = "user_id";
const sql = `UPDATE ${this.table}
SET email = $1, status = $2, flag = $3, last_modified = $4
@@ -146,7 +146,7 @@ IUpdateFlagRepository
}
}
- async updateFlag(id: string, dto: Partial): Promise {
+ async updateFlag(id: UsersId, dto: Partial): Promise {
const filterColumn = "user_id";
const sql = `UPDATE ${this.table}
SET flag = $1, last_modified = $2
diff --git a/backend/src/routes/meta.route.ts b/backend/src/routes/meta.route.ts
index 79ef5c7..26777e9 100644
--- a/backend/src/routes/meta.route.ts
+++ b/backend/src/routes/meta.route.ts
@@ -15,7 +15,7 @@ const router = Router();
// findById
router.get(
- '/by-id/:id',
+ '/id/:id',
authAdmin(),
byIdSchema,
factory(metaController.getMetaById)
@@ -23,7 +23,7 @@ router.get(
// findByName
router.get(
- '/by-name/:name',
+ '/name/:name',
authAdmin(),
byNameSchema,
factory(metaController.getMetaByName)
diff --git a/backend/src/routes/tickets.route.ts b/backend/src/routes/tickets.route.ts
index 3866b0f..f56083d 100644
--- a/backend/src/routes/tickets.route.ts
+++ b/backend/src/routes/tickets.route.ts
@@ -22,7 +22,7 @@ const router = Router();
// findById
router.get(
- '/by-id/:id',
+ '/id/:id',
authAdmin(),
getSchema,
factory(ticketsController.getTicket)
diff --git a/backend/src/routes/users.route.ts b/backend/src/routes/users.route.ts
index 181f349..67ec5cd 100644
--- a/backend/src/routes/users.route.ts
+++ b/backend/src/routes/users.route.ts
@@ -16,7 +16,7 @@ const router = Router();
// findById
router.get(
- '/by-id/:id',
+ '/id/:id',
authAdmin(),
byIdSchema,
factory(usersController.getUserById)
@@ -24,7 +24,7 @@ router.get(
// findByEmail
router.get(
- '/by-email/:email',
+ '/email/:email',
authAdmin(),
byEmailSchema,
factory(usersController.getUserByEmail)
diff --git a/backend/src/services/clients.service.ts b/backend/src/services/clients.service.ts
index 4876f8c..b0160ba 100644
--- a/backend/src/services/clients.service.ts
+++ b/backend/src/services/clients.service.ts
@@ -12,7 +12,7 @@ import {
import clientsModel from '../models/clients.model';
import * as CommonUtils from '../utils/common.utils';
import clientsRepository from '../repositories/clients.repository';
-import { Clients } from "../repositories/interfaces/clients.entity.interface";
+import { Clients, ClientsId } from "../repositories/interfaces/clients.entity.interface";
class ClientsService {
private timeMapTargets: string[];
@@ -24,7 +24,7 @@ class ClientsService {
/**
* @description Usage for testing purpose.
*/
- async getClientById(id: string): Promise {
+ async getClientById(id: ClientsId): Promise {
const result = await clientsRepository.findById(id);
return !result ? null : CommonUtils.mapObjTimestamps(result, this.timeMapTargets);
}
@@ -46,22 +46,22 @@ class ClientsService {
async createClient(dto: ClientsCreateDTO): Promise {
const clientsCreateObj = clientsModel.generateClientsCreateObj(dto);
const result = await clientsRepository.create(clientsCreateObj.client);
- return clientsModel.mapToCreateResponseDTO(result as Clients, clientsCreateObj.keyRaw);
+ return clientsModel.toClientsCreateResponseDTO(result as Clients, clientsCreateObj.keyRaw);
}
- async updateClientFlag(id: string, dto: ClientsFlagUpdateDTO): Promise {
+ async updateClientFlag(id: ClientsId, dto: ClientsFlagUpdateDTO): Promise {
dto.last_modified = CommonUtils.getTimestampUTC();
const result = await clientsRepository.updateFlag(id, dto);
return !result ? null : CommonUtils.mapObjTimestamps(result, this.timeMapTargets);
}
- async updateClientStatus(id: string, dto: ClientsStatusUpdateDTO): Promise {
+ async updateClientStatus(id: ClientsId, dto: ClientsStatusUpdateDTO): Promise {
dto.last_modified = CommonUtils.getTimestampUTC();
const result = await clientsRepository.updateStatus(id, dto);
return !result ? null : CommonUtils.mapObjTimestamps(result, this.timeMapTargets);
}
- async updateClientLastUse(id: string): Promise {
+ async updateClientLastUse(id: ClientsId): Promise {
const dto: ClientsLastUseUpdateDTO = { last_use: CommonUtils.getTimestampUTC() };
const result = await clientsRepository.updateLastUse(id, dto);
return !result ? null : CommonUtils.mapObjTimestamps(result, this.timeMapTargets);
diff --git a/backend/src/services/feedback-rating.service.ts b/backend/src/services/feedback-rating.service.ts
index 262a87d..b733ade 100644
--- a/backend/src/services/feedback-rating.service.ts
+++ b/backend/src/services/feedback-rating.service.ts
@@ -8,15 +8,10 @@ import { PoolClient } from 'pg';
import feedbackRatingModel from "../models/feedback-rating.model";
import feedbackRatingRepository from "../repositories/feedback-rating.repository";
import { FeedbackRating } from "../repositories/interfaces/feedback-rating.entity.interface";
+import { ClientsId } from "../repositories/interfaces/clients.entity.interface";
class FeedbackRatingService {
- private timeMapTargets: string[];
-
- constructor() {
- this.timeMapTargets = ['last_modified', 'created_on'];
- }
-
- async getExtendedFeedbackRatingById(id: string): Promise {
+ async getExtendedFeedbackRatingById(id: ClientsId): Promise {
const result: FeedbackRating | null = await feedbackRatingRepository.findById(id);
if(!result) {
return null;
@@ -55,7 +50,7 @@ class FeedbackRatingService {
/**
* @description Service function to call only when used within a transaction => needs PoolClient as param.
*/
- async updateFeedbackRatingInTa(client: PoolClient, id: string, dto: FeedbackRatingUpdateDTO): Promise {
+ async updateFeedbackRatingInTa(client: PoolClient, id: ClientsId, dto: FeedbackRatingUpdateDTO): Promise {
dto = feedbackRatingModel.mapFeedbackRatingUpdateDTO(dto);
const result: FeedbackRating | null = await feedbackRatingRepository.updateInTa(client, id, dto);
if(!result) {
diff --git a/backend/src/services/feedback.service.ts b/backend/src/services/feedback.service.ts
index aca3a74..87c1f40 100644
--- a/backend/src/services/feedback.service.ts
+++ b/backend/src/services/feedback.service.ts
@@ -9,12 +9,12 @@ import * as RepoUtils from "../utils/repository.utils";
import feedbackRatingModel from "../models/feedback-rating.model";
import feedbackModel from "../models/feedback.model";
import feedbackRepository from "../repositories/feedback.repository";
-import { Feedback } from "../repositories/interfaces/feedback.entity.interface";
+import { Feedback, FeedbackId } from "../repositories/interfaces/feedback.entity.interface";
import feedbackRatingService from "./feedback-rating.service";
+import { DBConstraintErrorException } from "../utils/exceptions/db.exception";
class FeedbackService {
-
- async getFeedbackById(id: number): Promise {
+ async getFeedbackById(id: FeedbackId): Promise {
const result = await feedbackRepository.findById(id);
return !result ? null : feedbackModel.toFeedbackResponseDTO(result);
}
@@ -37,6 +37,9 @@ class FeedbackService {
let dtoUpdateFR: FeedbackRatingUpdateDTO;
if(!result) {
return null;
+ } else if(result.blocked) {
+ // Update on feedback is not allowed if message exists for entry without being reviewed yet.
+ throw new DBConstraintErrorException('support-constraint-feedback');
} else if(new Date(result.created_on).getTime() === new Date(entity.created_on!).getTime()) {
// New Feedback was created => increase rating_sum.
dtoUpdateFR = { count: 1, rating: dto.rating };
@@ -56,7 +59,8 @@ class FeedbackService {
};
await feedbackRatingService.createFeedbackRatingInTa(client, dtoCreateFR);
}
- // Use rating from dto if no other ratings for this client exist.
+ // Delete blocked info and use rating from dto if no other ratings for this client exist.
+ delete result['blocked'];
return {
...result,
rating_average_new: update?.rating_average ?? dto.rating
@@ -64,7 +68,7 @@ class FeedbackService {
})
}
- async updateFeedbackReview(id: number): Promise {
+ async updateFeedbackReview(id: FeedbackId): Promise {
const dto: FeedbackUpdateReviewDTO = feedbackModel.generateFeedbackUpdateReviewDTO();
const result: Feedback | null = await feedbackRepository.updateReview(id, dto);
return !result ? null : feedbackModel.toFeedbackResponseDTO(result);
diff --git a/backend/src/services/meta.service.ts b/backend/src/services/meta.service.ts
index 1fa3f47..9020b6a 100644
--- a/backend/src/services/meta.service.ts
+++ b/backend/src/services/meta.service.ts
@@ -4,6 +4,7 @@ import {
MetaUpdateDTO,
MaintenanceUpdateDTO,
} from '../dtos/meta.dto';
+import { MetaId } from '../repositories/interfaces/meta.entity.interface';
import metaRepository from '../repositories/meta.repository';
import * as CommonUtils from "../utils/common.utils";
@@ -14,7 +15,7 @@ class MetaService {
this.timeMapTargets = ['build_on', 'created_on', 'last_modified'];
}
- async getMetaById(id: number): Promise {
+ async getMetaById(id: MetaId): Promise {
const result = await metaRepository.findById(id);
return !result ? null : CommonUtils.mapObjTimestamps(result, this.timeMapTargets);
}
@@ -29,7 +30,7 @@ class MetaService {
return !result ? null : CommonUtils.mapArrayTimestamps(result, this.timeMapTargets);
}
- async updateMeta(id: number, dto: MetaUpdateDTO): Promise {
+ async updateMeta(id: MetaId, dto: MetaUpdateDTO): Promise {
dto.last_modified = CommonUtils.getTimestampUTC();
const result = await metaRepository.update(id, dto);
return !result ? null : CommonUtils.mapObjTimestamps(result, this.timeMapTargets);
@@ -40,7 +41,7 @@ class MetaService {
return !result ? null : CommonUtils.mapObjTimestamps(result, this.timeMapTargets);
}
- async updateMaintenanceMode(id: number, dto: MaintenanceUpdateDTO): Promise {
+ async updateMaintenanceMode(id: MetaId, dto: MaintenanceUpdateDTO): Promise {
dto.last_modified = CommonUtils.getTimestampUTC();
const result = await metaRepository.updateMaintenance(id, dto);
return !result ? null : CommonUtils.mapObjTimestamps(result, this.timeMapTargets);
diff --git a/backend/src/services/test.service.ts b/backend/src/services/test.service.ts
index bc86aac..fd4c4df 100644
--- a/backend/src/services/test.service.ts
+++ b/backend/src/services/test.service.ts
@@ -1,8 +1,8 @@
-import { MetaResponseDTO } from '../dtos/meta.dto';
import { TestDemoDTO, TestErrorDTO } from '../dtos/test.dto';
import testModel from '../models/test.model';
import metaRepository from '../repositories/meta.repository';
import { DemoMode } from '../utils/enums/demo-mode.enum';
+import { secrets } from '../utils/secrets.utils';
class TestService {
async searchExceptionThrow(dto: TestErrorDTO) {
@@ -13,8 +13,7 @@ class TestService {
let result: any;
switch(dto.demo_mode) {
case(DemoMode.SUCCESS): {
- result = await metaRepository.findById(1);
- result = { app_version: (result as MetaResponseDTO).app_version };
+ result = { app_version: secrets.APP_VERSION };
break;
}
case(DemoMode.ERROR):
diff --git a/backend/src/services/tickets.service.ts b/backend/src/services/tickets.service.ts
index f63e56f..2a2eacf 100644
--- a/backend/src/services/tickets.service.ts
+++ b/backend/src/services/tickets.service.ts
@@ -7,6 +7,7 @@ import {
TicketsIntervalDTO
} from "../dtos/tickets.dto";
import ticketsModel from "../models/tickets.model";
+import { TicketsId } from "../repositories/interfaces/tickets.entity.interface";
import ticketsRepository from "../repositories/tickets.repository";
import * as CommonUtils from "../utils/common.utils";
@@ -17,7 +18,7 @@ class TicketsService {
this.timeMapTargets = ['last_modified', 'created_on'];
}
- async getTicketById(id: string): Promise {
+ async getTicketById(id: TicketsId): Promise {
const result = await ticketsRepository.findById(id);
return !result ? null : CommonUtils.mapObjTimestamps(result, this.timeMapTargets);
}
@@ -38,18 +39,18 @@ class TicketsService {
}
async createTicket(dto: TicketsCreateDTO, files: Express.Multer.File[] | null): Promise {
- const ticket = await ticketsModel.generateTicket(dto, files);
+ const ticket = await ticketsModel.generateTicketEntity(dto, files);
const result = await ticketsRepository.create(ticket);
return CommonUtils.mapObjTimestamps(result, this.timeMapTargets);
}
- async updateTicket(id: string, dto: TicketsUpdateDTO): Promise {
+ async updateTicket(id: TicketsId, dto: TicketsUpdateDTO): Promise {
dto = ticketsModel.mapTicketUpdateDto(dto);
const result = await ticketsRepository.update(id, dto);
return !result ? null : CommonUtils.mapObjTimestamps(result, this.timeMapTargets);
}
- async deleteTicket(id: string): Promise {
+ async deleteTicket(id: TicketsId): Promise {
const ticket = await ticketsRepository.findById(id);
if(ticket && ticketsModel.isPermittedToDelete(ticket)) {
await ticketsModel.handleTicketBeforeDelete(ticket);
diff --git a/backend/src/services/users.service.ts b/backend/src/services/users.service.ts
index 372d6e2..d740749 100644
--- a/backend/src/services/users.service.ts
+++ b/backend/src/services/users.service.ts
@@ -8,7 +8,7 @@ import {
import usersRepository from "../repositories/users.repository";
import * as CommonUtils from "../utils/common.utils";
import usersModel from "../models/users.model";
-import { Users } from "../repositories/interfaces/users.entity.interface";
+import { Users, UsersId } from "../repositories/interfaces/users.entity.interface";
class UsersService {
private timeMapTargets: string[];
@@ -17,7 +17,7 @@ class UsersService {
this.timeMapTargets = ['last_modified', 'created_on'];
}
- async getUserById(id: string): Promise {
+ async getUserById(id: UsersId): Promise {
const result = await usersRepository.findById(id);
return !result ? null : CommonUtils.mapObjTimestamps(result, this.timeMapTargets);
}
@@ -38,18 +38,18 @@ class UsersService {
}
async createUser(dto: UsersCreateDTO): Promise {
- const user: Users = usersModel.generateUser(dto);
+ const user: Users = usersModel.generateUserEntity(dto);
const result = await usersRepository.create(user);
return CommonUtils.mapObjTimestamps(result, this.timeMapTargets)
}
- async updateUser(id: string, dto: UsersUpdateDTO): Promise {
+ async updateUser(id: UsersId, dto: UsersUpdateDTO): Promise {
dto.last_modified = CommonUtils.getTimestampUTC();
const result = await usersRepository.update(id, dto);
return !result ? null : CommonUtils.mapObjTimestamps(result, this.timeMapTargets);
}
- async updateUserFlag(id: string, dto: UsersFlagUpdateDTO): Promise {
+ async updateUserFlag(id: UsersId, dto: UsersFlagUpdateDTO): Promise {
dto.last_modified = CommonUtils.getTimestampUTC();
const result = await usersRepository.updateFlag(id, dto);
return !result ? null : CommonUtils.mapObjTimestamps(result, this.timeMapTargets);
diff --git a/backend/src/utils/common.utils.ts b/backend/src/utils/common.utils.ts
index d625aec..49aefc1 100644
--- a/backend/src/utils/common.utils.ts
+++ b/backend/src/utils/common.utils.ts
@@ -13,8 +13,8 @@ const logger = Logger.getLogger();
*/
export const now = (): Date => new Date();
-export function generateUUID(): string {
- return uuid_v4();
+export function generateUUID(): T {
+ return uuid_v4() as T;
}
export function mapKeyToHash(key: string): string {
@@ -40,19 +40,6 @@ export function isEmptyObj(obj: any): boolean {
return JSON.stringify(obj) === '{}';
}
-export function selectPrivateKey(source: MailSource): string {
- switch(source) {
- case(MailSource.ARTDV): {
- return secrets.PRIVATE_KEY_ARTDV;
- }
- case(MailSource.TAVA): {
- return secrets.PRIVATE_KEY_TAVA;
- }
- default:
- throw new InvalidSourceException();
- }
-}
-
export function logError(message: string, method: string, err: any) {
logger.error(message, {
error: err.error,
diff --git a/backend/src/utils/custom-types.utils.ts b/backend/src/utils/custom-types.utils.ts
index 50368d7..a14bdec 100644
--- a/backend/src/utils/custom-types.utils.ts
+++ b/backend/src/utils/custom-types.utils.ts
@@ -1,5 +1,3 @@
import { Request, Response, NextFunction } from "express";
-export type AsyncMiddleware = (req: Request, res: Response, next: NextFunction) => Promise;
-
-export type SingleOrArray = T | T[];
\ No newline at end of file
+export type AsyncMiddleware = (req: Request, res: Response, next: NextFunction) => Promise;
\ No newline at end of file
diff --git a/backend/src/utils/exceptions/db.exception.ts b/backend/src/utils/exceptions/db.exception.ts
index 4a93120..476eed6 100644
--- a/backend/src/utils/exceptions/db.exception.ts
+++ b/backend/src/utils/exceptions/db.exception.ts
@@ -38,4 +38,10 @@ export class DBQueryErrorException extends DBException {
constructor(data?: any) {
super('support-dbquery-error', data);
}
+}
+
+export class DBConstraintErrorException extends DBException {
+ constructor(message: string, data?: any) {
+ super(message, data);
+ }
}
\ No newline at end of file
diff --git a/backend/src/utils/repository.utils.ts b/backend/src/utils/repository.utils.ts
index 0ca5af9..cf6f15d 100644
--- a/backend/src/utils/repository.utils.ts
+++ b/backend/src/utils/repository.utils.ts
@@ -26,7 +26,7 @@ export function mapFilteredQueryValues>(dto: T, ta
const argGroups: string[] = [];
const timestampObj = {};
Object.entries(dto).forEach(([key, content]) => {
- if(key !== 'reviewed_on' && key !== 'last_modified' && key !== 'created_on') {
+ if(key !== 'last_modified' && key !== 'created_on') {
const valArr = Array.isArray(content) ? content : [content];
const conditions = valArr.map((value) => {
if(value === null) {
@@ -81,7 +81,11 @@ export async function asTransaction(
} catch(err: any) {
await client.query('ROLLBACK');
logError(message, method, err);
- throw new DBQueryErrorException(err);
+ if(err.error) {
+ throw err;
+ } else {
+ throw new DBQueryErrorException(err);
+ }
} finally {
await db.close(client);
}
diff --git a/backend/src/utils/secrets.utils.ts b/backend/src/utils/secrets.utils.ts
index ba06cef..06f27ae 100644
--- a/backend/src/utils/secrets.utils.ts
+++ b/backend/src/utils/secrets.utils.ts
@@ -6,14 +6,12 @@ import fs from "fs";
class Secrets {
readonly APP_VERSION: string;
readonly APP_META: Record;
- readonly ADMIN_API: string;
+ readonly ADMIN_KEY: string;
readonly ENV_MODE: string;
readonly PORT: number;
readonly EMAIL_RECEIVER: string;
readonly EMAIL_SENDER: string;
readonly EMAIL_PASS: string;
- readonly PRIVATE_KEY_ARTDV: string;
- readonly PRIVATE_KEY_TAVA: string;
readonly BETTERSTACK_LOGGING_KEY: string;
readonly BETTERSTACK_HOST: string;
readonly DB_USER: string;
@@ -44,14 +42,12 @@ class Secrets {
private constructor() {
this.APP_VERSION = this.getAppVersion();
this.APP_META = this.getAppMeta();
- this.ADMIN_API = this.setAdminApi();
+ this.ADMIN_KEY = this.setAdminKey();
this.ENV_MODE = this.setEnvMode();
this.PORT = this.setPort();
this.EMAIL_RECEIVER = this.setEmailReceiver();
this.EMAIL_SENDER = this.setEmailSender();
this.EMAIL_PASS = this.setEmailPass();
- this.PRIVATE_KEY_ARTDV = this.setPrivateKey_ARTDV();
- this.PRIVATE_KEY_TAVA = this.setPrivateKey_TAVA();
this.BETTERSTACK_LOGGING_KEY = this.setBetterStackLoggingKey();
this.BETTERSTACK_HOST = this.setBetterStackHost();
this.DB_USER = this.setDbUser();
@@ -97,11 +93,11 @@ class Secrets {
return !packageJson ? null : packageJson.appMeta;
}
- private setAdminApi() {
- if(!Config.ADMIN_API) {
- throw new AuthSecretNotFoundException('secret-404-env#ADMIN_API');
+ private setAdminKey() {
+ if(!Config.ADMIN_KEY) {
+ throw new AuthSecretNotFoundException('secret-404-env#ADMIN_KEY');
}
- return Config.ADMIN_API;
+ return Config.ADMIN_KEY;
}
private setEnvMode() {
@@ -139,34 +135,6 @@ class Secrets {
return Config.EMAIL_PASS;
}
- private setPrivateKey_ARTDV = () => {
- let key;
- if(Config.MODE === 'development') {
- key = !Config.PRIVATE_KEY_ARTDV ? null : fs.readFileSync(Config.PRIVATE_KEY_ARTDV, 'utf8');
- } else {
- key = Config.PRIVATE_KEY_ARTDV;
- }
-
- if(!key) {
- throw new AuthSecretNotFoundException('secret-404-env#PRIVATE_KEY_ARTDV');
- }
- return key;
- }
-
- private setPrivateKey_TAVA = () => {
- let key;
- if(Config.MODE === 'development') {
- key = !Config.PRIVATE_KEY_TAVA ? null : fs.readFileSync(Config.PRIVATE_KEY_TAVA, 'utf8');
- } else {
- key = Config.PRIVATE_KEY_TAVA;
- }
-
- if(!key) {
- throw new AuthSecretNotFoundException('secret-404-env#PRIVATE_KEY_TAVA');
- }
- return key;
- }
-
private setBetterStackLoggingKey = () => {
if(!Config.BETTERSTACK_LOGGING_KEY) {
throw new AuthSecretNotFoundException('secret-404-env#BETTERSTACK_LOGGING_KEY');
diff --git a/backend/src/validation/schemata/tickets.schema.validation.ts b/backend/src/validation/schemata/tickets.schema.validation.ts
index 24aab0f..424ac08 100644
--- a/backend/src/validation/schemata/tickets.schema.validation.ts
+++ b/backend/src/validation/schemata/tickets.schema.validation.ts
@@ -2,7 +2,6 @@ import { body, param, ValidationChain } from 'express-validator';
import * as CommonValidators from "../common.validation";
import { Flag } from '../../utils/enums/flag.enum';
import { TicketStatus } from '../../utils/enums/ticket-status.enum';
-import { SingleOrArray } from '../../utils/custom-types.utils';
import { CommonExceptionMessage as Message } from '../../utils/enums/common-exception-messages.enum';
import { TicketOption } from '../../utils/enums/ticket-option.enum';
@@ -36,21 +35,21 @@ export const postTicketsSearchSchema: ValidationChain[] = [
.withMessage('support-invalid-max#title!100')
.optional(),
body('status')
- .custom((content: SingleOrArray) => {
+ .custom((content: TicketStatus | TicketStatus[]) => {
content = Array.isArray(content) ? content : [content];
content.forEach((status) => CommonValidators.validateEnum(status, TicketStatus, 'ticketStatus'))
return true;
})
.optional(),
body('option')
- .custom((content: SingleOrArray) => {
+ .custom((content: TicketOption | TicketOption[]) => {
content = Array.isArray(content) ? content : [content];
content.forEach((option) => CommonValidators.validateEnum(option, TicketOption, 'ticketOption'))
return true;
})
.optional(),
body('flag')
- .custom((content: undefined | null | SingleOrArray) => {
+ .custom((content: undefined | null | Flag | Flag[]) => {
// Manual check for undefined/null necessary because null is valid value.
if(content === null || content === undefined) {
return true;
diff --git a/backend/src/validation/schemata/users.schema.validation.ts b/backend/src/validation/schemata/users.schema.validation.ts
index aead4b3..3a08eb1 100644
--- a/backend/src/validation/schemata/users.schema.validation.ts
+++ b/backend/src/validation/schemata/users.schema.validation.ts
@@ -2,7 +2,6 @@ import { body, param, ValidationChain } from 'express-validator';
import * as CommonValidators from "../common.validation";
import { UserStatus } from '../../utils/enums/user-status.enum';
import { Flag } from '../../utils/enums/flag.enum';
-import { SingleOrArray } from '../../utils/custom-types.utils';
import { CommonExceptionMessage as Message } from '../../utils/enums/common-exception-messages.enum';
export const getUserByIdSchema: ValidationChain[] = [
@@ -22,21 +21,21 @@ export const getUserByEmailSchema: ValidationChain[] = [
export const postUsersSearchSchema: ValidationChain[] = [
body('email')
- .custom((content: SingleOrArray) => {
+ .custom((content: string | string[]) => {
content = Array.isArray(content) ? content : [content];
content.forEach((email) => CommonValidators.validateEmail(email))
return true;
})
.optional(),
body('status')
- .custom((content: SingleOrArray) => {
+ .custom((content: UserStatus | UserStatus[]) => {
content = Array.isArray(content) ? content : [content];
content.forEach((status) => CommonValidators.validateEnum(status, UserStatus, 'userStatus'))
return true;
})
.optional(),
body('flag')
- .custom((content: undefined | null | SingleOrArray) => {
+ .custom((content: undefined | null | Flag | Flag[]) => {
// Manual check for undefined/null necessary because null is valid value.
if(content === null || content === undefined) {
return true;
diff --git a/backend/tests/common.test-utils.ts b/backend/tests/common.test-utils.ts
index d50c945..b1cb1ab 100644
--- a/backend/tests/common.test-utils.ts
+++ b/backend/tests/common.test-utils.ts
@@ -1,6 +1,8 @@
import { QueryResult } from "pg";
import { DBConnection } from "../src/configs/db";
import { NextFunction, Request, Response } from "express";
+import { UsersId } from "../src/repositories/interfaces/users.entity.interface";
+import { ClientsId } from "../src/repositories/interfaces/clients.entity.interface";
type MockClient = {
query: jest.Mock
@@ -40,7 +42,7 @@ export function disableConsoleMessages() {
/**
* @description Used in combination with createTestApp() to mock client authentication for flexible testing.
*/
-export function injectTestClientId(clientId: string) {
+export function injectTestClientId(clientId: ClientsId) {
return function (req: Request, res: Response, next: NextFunction) {
(req as any).apiClients = { client_id: clientId };
next();
@@ -50,7 +52,7 @@ export function injectTestClientId(clientId: string) {
/**
* @description Used in combination with createTestApp() to mock user authentication for flexible testing.
*/
-export function injectTestUserId(userId: string) {
+export function injectTestUserId(userId: UsersId) {
return function (req: Request, res: Response, next: NextFunction) {
(req as any).apiUsers = { user_id: userId };
next();
diff --git a/backend/tests/integration-tests/middleware/penalty.integration.test.ts b/backend/tests/integration-tests/middleware/penalty.integration.test.ts
index 7b9fc1b..16033ec 100644
--- a/backend/tests/integration-tests/middleware/penalty.integration.test.ts
+++ b/backend/tests/integration-tests/middleware/penalty.integration.test.ts
@@ -10,12 +10,15 @@ import { runMigrations } from "../../db-migrations.setup";
import usersService from "../../../src/services/users.service";
import { MaintenanceMode } from "../../../src/utils/enums/maintenance-mode.enum";
import metaService from "../../../src/services/meta.service";
+import { UsersId } from "../../../src/repositories/interfaces/users.entity.interface";
+import { ClientsId } from "../../../src/repositories/interfaces/clients.entity.interface";
+import { MetaId } from "../../../src/repositories/interfaces/meta.entity.interface";
jest.setTimeout(60000);
-const testValidMetaId = mockId.meta.valid[0];
-const testValidClientsId = mockId.clients.valid[0];
-const testValidUsersId = mockId.users.valid[0];
+const testValidMetaId = mockId.meta.valid[0] as MetaId;
+const testValidClientId = mockId.clients.valid[0] as ClientsId;
+const testValidUserId = mockId.users.valid[0] as UsersId;
describe('Integration-tests (middleware), priority: class PenaltyHandler', () => {
@@ -40,13 +43,13 @@ describe('Integration-tests (middleware), priority: class PenaltyHandler', () =>
test('Params: has Violation.CLIENTSFLAG, result: "PENALTY"', async () => {
const testParam_context: PenaltyContext = {
type: Violation.CLIENTSFLAG,
- id: testValidClientsId,
+ id: testValidClientId,
penaltyValue: null
};
await dbTestSetup.addTestData();
const _ = await penaltyHandler.apply(testParam_context);
- const testFn = await clientsService.getClientById(testValidClientsId);
+ const testFn = await clientsService.getClientById(testValidClientId);
const testClientsFlagResult = Flag.WARNING;
expect(testFn?.flag).toBe(testClientsFlagResult);
@@ -55,13 +58,13 @@ describe('Integration-tests (middleware), priority: class PenaltyHandler', () =>
test('Params: has Violation.USERSFLAG, result: "PENALTY"', async () => {
const testParam_context: PenaltyContext = {
type: Violation.USERSFLAG,
- id: testValidUsersId,
+ id: testValidUserId,
penaltyValue: null
};
await dbTestSetup.addTestData();
const _ = await penaltyHandler.apply(testParam_context);
- const testFn = await usersService.getUserById(testValidUsersId);
+ const testFn = await usersService.getUserById(testValidUserId);
const testUsersFlagResult = Flag.WARNING;
expect(testFn?.flag).toBe(testUsersFlagResult);
diff --git a/backend/tests/integration-tests/repositories/clients.integration.test.ts b/backend/tests/integration-tests/repositories/clients.integration.test.ts
index a868924..e3831bc 100644
--- a/backend/tests/integration-tests/repositories/clients.integration.test.ts
+++ b/backend/tests/integration-tests/repositories/clients.integration.test.ts
@@ -16,7 +16,7 @@ import { ApiKeyStatus } from "../../../src/utils/enums/api-key-status.enum";
import { DBTestSetup } from "../../db-container.setup";
import { runMigrations } from '../../db-migrations.setup';
import clientsModel from "../../../src/models/clients.model";
-import { Clients } from "../../../src/repositories/interfaces/clients.entity.interface";
+import { Clients, ClientsId } from "../../../src/repositories/interfaces/clients.entity.interface";
import { CommonExceptionMessage } from "../../../src/utils/enums/common-exception-messages.enum";
import { default as mockId } from "../../mock-data/id.mock-data.json";
import { secrets } from "../../../src/utils/secrets.utils";
@@ -33,6 +33,8 @@ jest.mock('../../../src/middleware/maintenance.middleware', () => ({
import app from '../../../src/app';
jest.setTimeout(60000);
+
+const mockValidClientId = mockId.clients.valid[0] as ClientsId;
const testTimestamp = '2025-01-01T14:00:02.000Z';
describe('Integration-tests (repository), priority: entity Clients', () => {
@@ -59,9 +61,9 @@ describe('Integration-tests (repository), priority: entity Clients', () => {
describe('Testing valid fn calls', () => {
test('Repository process fn findById(), result: "SUCCESS"', async () => {
- const testParam_id = mockId.clients.valid[0];
+ const testParam_id = mockValidClientId;
const testResult: ClientsExistResponseDTO | null = {
- client_id: mockId.clients.valid[0],
+ client_id: testParam_id,
name: 'TESTCLIENT',
api_key_hash: secrets.TEST_APIKEY_HASH,
status: ApiKeyStatus.ACTIVE,
@@ -80,7 +82,7 @@ describe('Integration-tests (repository), priority: entity Clients', () => {
test('Repository process fn findStatusByName(), result: "SUCCESS"', async () => {
const testParam_name = 'TESTCLIENT';
const testResult: ClientsStatusResponseDTO = {
- client_id: mockId.clients.valid[0],
+ client_id: mockValidClientId,
name: testParam_name,
status: ApiKeyStatus.ACTIVE,
last_use: testTimestamp,
@@ -109,11 +111,11 @@ describe('Integration-tests (repository), priority: entity Clients', () => {
})
test('Repository process fn create(), result: "SUCCESS"', async () => {
- const testParam_client_id = mockId.clients.new[0];
+ const testParam_client_id = mockId.clients.new[0] as ClientsId;
const testParam_dto = { name: 'testclient_test_create' };
jest.spyOn(CommonUtils, 'generateUUID').mockReturnValue(testParam_client_id);
- jest.spyOn(clientsModel, '_generateApiKeyObj').mockReturnValue(testVar_apiKey);
+ jest.spyOn(clientsModel as any, 'generateApiKeyObj').mockReturnValue(testVar_apiKey);
jest.spyOn(CommonUtils, "getTimestampUTC").mockReturnValue(testTimestamp);
const testResult: ClientsCreateResponseDTO = {
@@ -137,7 +139,7 @@ describe('Integration-tests (repository), priority: entity Clients', () => {
})
test('Repository process fn updateFlag(), result: "SUCCESS"', async () => {
- const testParam_id = mockId.clients.valid[0];
+ const testParam_id = mockValidClientId;
const testParam_dto: ClientsFlagUpdateDTO = {
flag: Flag.WARNING
};
@@ -159,7 +161,7 @@ describe('Integration-tests (repository), priority: entity Clients', () => {
})
test('Repository process fn updateFlag(), result: null', async () => {
- const testParam_id = mockId.clients.invalid[0];
+ const testParam_id = mockId.clients.invalid[0] as ClientsId;
const testParam_dto: ClientsFlagUpdateDTO = {
flag: Flag.WARNING
};
@@ -175,7 +177,7 @@ describe('Integration-tests (repository), priority: entity Clients', () => {
})
test('Repository process fn updateStatus(), result: "SUCCESS"', async () => {
- const testParam_id = mockId.clients.valid[0];
+ const testParam_id = mockValidClientId;
const testParam_data: Partial = {
status: ApiKeyStatus.DISABLED
};
@@ -208,7 +210,7 @@ describe('Integration-tests (repository), priority: entity Clients', () => {
describe('Route: PUT/status/:id', () => {
test('Params: , validator: fn isUUID() by invalid id', async () => {
- const testParam_id = 'invalid-id';
+ const testParam_id = 'invalid-UUID' as ClientsId;
const testParam_dto: ClientsStatusUpdateDTO = {
status: ApiKeyStatus.EXPIRED
};
@@ -304,7 +306,7 @@ describe('Integration-tests (repository), priority: entity Clients', () => {
describe('Route: PUT/status/:id', () => {
test('Params: , validator: fn requirePayload() by undefined', async () =>{
- const testParam_id = mockId.clients.valid[0];
+ const testParam_id = mockValidClientId;
const testParam_dto = undefined;
const testError = structuredClone(mockError);
diff --git a/backend/tests/integration-tests/repositories/feedback-rating.integration.test.ts b/backend/tests/integration-tests/repositories/feedback-rating.integration.test.ts
index 56692be..880a181 100644
--- a/backend/tests/integration-tests/repositories/feedback-rating.integration.test.ts
+++ b/backend/tests/integration-tests/repositories/feedback-rating.integration.test.ts
@@ -6,13 +6,15 @@ import { runMigrations } from '../../db-migrations.setup';
import { default as mockId } from "../../mock-data/id.mock-data.json";
import { FeedbackRatingExtendedResponseDTO, FeedbackRatingResponseDTO } from "../../../src/dtos/feedback-rating.dto";
import { ErrorStatusCodes } from "../../../src/utils/errorStatusCodes.utils";
+import { DBTestData } from "../../db-data.setup";
+import { ClientsId } from "../../../src/repositories/interfaces/clients.entity.interface";
jest.mock('../../../src/middleware/auth.admin.middleware', () => ({
authAdmin: jest.fn(() => (req: Request, res: Response, next: NextFunction) => next())
}));
jest.mock('../../../src/middleware/auth.client.middleware', () => ({
authClient: jest.fn(() => (req: Request, res: Response, next: NextFunction) => {
- (req as any).apiClients = { client_id: mockId.clients.valid[0] };
+ (req as any).apiClients = { client_id: testValidClientId };
next();
})
}));
@@ -21,10 +23,10 @@ jest.mock('../../../src/middleware/maintenance.middleware', () => ({
}));
import app from '../../../src/app';
-import { DBTestData } from "../../db-data.setup";
jest.setTimeout(60000);
+const testValidClientId = mockId.clients.valid[0] as ClientsId;
const testTimestamp = '2025-01-01T14:00:09.000Z';
describe('Integration-tests (repository), priority: entity FeedbackRating', () => {
@@ -53,7 +55,7 @@ describe('Integration-tests (repository), priority: entity FeedbackRating', () =
describe('Testing valid fn calls', () => {
test('Repository process fn findById(), result: "SUCCESS"', async () => {
- const testParam_id = mockId.clients.valid[0];
+ const testParam_id = testValidClientId;
const testResult: FeedbackRatingExtendedResponseDTO | null = {
client_id: testParam_id,
count: dbData_FeedbackRating[1],
@@ -89,7 +91,7 @@ describe('Integration-tests (repository), priority: entity FeedbackRating', () =
test('Repository process fn findAll(), result: "SUCCESS"', async () => {
const testResult: FeedbackRatingExtendedResponseDTO[] | null = [
{
- client_id: mockId.clients.valid[0],
+ client_id: testValidClientId,
count: dbData_FeedbackRating[1],
rating_sum: dbData_FeedbackRating[2],
rating_average: 4.2,
@@ -116,7 +118,7 @@ describe('Integration-tests (repository), priority: entity FeedbackRating', () =
describe('Route: GET/id/:id', () => {
test('Params: , validator: fn isUUID() by invalid id', async () => {
- const testParam_id = 'invalid-id';
+ const testParam_id = 'invalid-UUID' as ClientsId;
const testError = {
type: 'field',
value: testParam_id,
diff --git a/backend/tests/integration-tests/repositories/feedback.integration.test.ts b/backend/tests/integration-tests/repositories/feedback.integration.test.ts
index dee45d9..b12b074 100644
--- a/backend/tests/integration-tests/repositories/feedback.integration.test.ts
+++ b/backend/tests/integration-tests/repositories/feedback.integration.test.ts
@@ -14,12 +14,11 @@ import router from '../../../src/routes/feedback.route';
import feedbackRatingService from '../../../src/services/feedback-rating.service';
import feedbackRepository from '../../../src/repositories/feedback.repository';
import feedbackService from '../../../src/services/feedback.service';
-import { DBQueryErrorException } from '../../../src/utils/exceptions/db.exception';
+import { DBConstraintErrorException, DBQueryErrorException } from '../../../src/utils/exceptions/db.exception';
import feedbackRatingRepository from '../../../src/repositories/feedback-rating.repository';
-
-const testValidClientsId = mockId.clients.valid[0];
-const testValidUsersId = mockId.users.valid[0];
-const testTimestamp = '2025-01-01T14:00:08.000Z';
+import { ClientsId } from '../../../src/repositories/interfaces/clients.entity.interface';
+import { UsersId } from '../../../src/repositories/interfaces/users.entity.interface';
+import { FeedbackId } from '../../../src/repositories/interfaces/feedback.entity.interface';
jest.mock('../../../src/middleware/auth.admin.middleware', () => ({
authAdmin: jest.fn(() => (req: Request, res: Response, next: NextFunction) => next())
@@ -39,6 +38,11 @@ jest.mock('../../../src/middleware/observe.middleware.ts', () => ({
jest.setTimeout(60000);
+const testValidFeedbackId = mockId.feedback.valid[0] as FeedbackId;
+const testValidClientId = mockId.clients.valid[0] as ClientsId;
+const testValidUserId = mockId.users.valid[0] as UsersId;
+const testTimestamp = '2025-01-01T14:00:08.000Z';
+
describe('Integration-tests (repository), priority: entity Feedback', () => {
let dbTestSetup: DBTestSetup;
@@ -70,11 +74,11 @@ describe('Integration-tests (repository), priority: entity Feedback', () => {
test('Repository process fn findById(), result: "SUCCESS"', async () => {
const app = createTestApp([], router, apiUrl);
- const testParam_id = mockId.feedback.valid[0];
+ const testParam_id = testValidFeedbackId;
const testResult: FeedbackResponseDTO | null = {
feedback_id: testParam_id,
- client_id: testValidClientsId,
- user_id: testValidUsersId,
+ client_id: testValidClientId,
+ user_id: testValidUserId,
rating: dbData_Feedback[2],
term_accepted: dbData_Feedback[3],
message: dbData_Feedback[4],
@@ -108,8 +112,8 @@ describe('Integration-tests (repository), priority: entity Feedback', () => {
test('Repository process fn upsertInTa(), result: new Feedback on existing FeedbackRating', async () => {
const app = createTestApp([
- MockUtils.injectTestClientId(mockId.clients.valid[0]),
- MockUtils.injectTestUserId(mockId.users.valid[1])
+ MockUtils.injectTestClientId(testValidClientId),
+ MockUtils.injectTestUserId(mockId.users.valid[1] as UsersId)
], router, apiUrl);
const email = dbTestData.getUsersInsertSql().values[7]; // name, 2nd insert-row
const testParam_dto: FeedbackRequestCreateDTO = {
@@ -123,9 +127,9 @@ describe('Integration-tests (repository), priority: entity Feedback', () => {
jest.spyOn(CommonUtils, "getTimestampUTC").mockReturnValue(testTimestamp);
const testResult: FeedbackResponseDTO = {
- feedback_id: mockId.feedback.new[0],
- client_id: testValidClientsId,
- user_id: mockId.users.valid[1],
+ feedback_id: mockId.feedback.new[0] as FeedbackId,
+ client_id: testValidClientId,
+ user_id: mockId.users.valid[1] as UsersId,
rating: testParam_dto.rating,
rating_average_new: mockResult_average_rating,
term_accepted: testParam_dto.term_accepted,
@@ -144,8 +148,8 @@ describe('Integration-tests (repository), priority: entity Feedback', () => {
test('Repository process fn upsertInTa(), result: new Feedback & FeedbackRating', async () => {
const app = createTestApp([
- MockUtils.injectTestClientId(mockId.clients.valid[1]),
- MockUtils.injectTestUserId(mockId.users.valid[0])
+ MockUtils.injectTestClientId(mockId.clients.valid[1] as ClientsId),
+ MockUtils.injectTestUserId(testValidUserId)
], router, apiUrl);
const email = dbTestData.getUsersInsertSql().values[7];
const testParam_dto: FeedbackRequestCreateDTO = {
@@ -158,9 +162,9 @@ describe('Integration-tests (repository), priority: entity Feedback', () => {
jest.spyOn(CommonUtils, "getTimestampUTC").mockReturnValue(testTimestamp);
const testResult: FeedbackResponseDTO = {
- feedback_id: mockId.feedback.new[0],
- client_id: mockId.clients.valid[1],
- user_id: mockId.users.valid[0],
+ feedback_id: mockId.feedback.new[0] as FeedbackId,
+ client_id: mockId.clients.valid[1] as ClientsId,
+ user_id: testValidUserId,
rating: testParam_dto.rating,
rating_average_new: testParam_dto.rating,
term_accepted: testParam_dto.term_accepted,
@@ -174,7 +178,7 @@ describe('Integration-tests (repository), priority: entity Feedback', () => {
.send(testParam_dto);
const testResponse_FeedbackRating =
- await feedbackRatingService.getExtendedFeedbackRatingById(mockId.clients.valid[1]);
+ await feedbackRatingService.getExtendedFeedbackRatingById(mockId.clients.valid[1] as ClientsId);
expect(testResponse_Feedback.statusCode).toBe(200);
expect(testResponse_Feedback.body).toMatchObject(testResult);
@@ -184,15 +188,14 @@ describe('Integration-tests (repository), priority: entity Feedback', () => {
test('Repository process fn upsertInTa(), result: update existing Feedback & FeedbackRating', async () => {
const app = createTestApp([
- MockUtils.injectTestClientId(mockId.clients.valid[0]),
- MockUtils.injectTestUserId(mockId.users.valid[0])
+ MockUtils.injectTestClientId(testValidClientId),
+ MockUtils.injectTestUserId(testValidUserId)
], router, apiUrl);
const email = dbTestData.getUsersInsertSql().values[1];
const testParam_dto: FeedbackRequestCreateDTO = {
user_email: email,
rating: 2,
term_accepted: true,
- message: 'test-feedback-message-updated-client[0]'
};
// Test data FeedbackRating => position (count): [1], position (rating_sum): [2]
const ratingDifference = testParam_dto.rating - dbTestData.getFeedbackInsertSql().values[2];
@@ -203,24 +206,25 @@ describe('Integration-tests (repository), priority: entity Feedback', () => {
jest.spyOn(CommonUtils, "getTimestampUTC").mockReturnValue(testTimestamp_update);
const testResult: FeedbackResponseDTO = {
- feedback_id: mockId.feedback.valid[0],
- client_id: mockId.clients.valid[0],
- user_id: mockId.users.valid[0],
+ feedback_id: testValidFeedbackId,
+ client_id: testValidClientId,
+ user_id: testValidUserId,
rating: testParam_dto.rating,
rating_average_new: mockResult_average_rating,
term_accepted: testParam_dto.term_accepted,
- message: testParam_dto.message,
last_modified: testTimestamp_update,
- created_on: testTimestamp
+ created_on: testTimestamp,
};
await dbTestSetup.addTestData();
+ // Update reviewed_on from NULL to value, otherwise WHERE clause sets blocked:true => throw exception.
+ await feedbackService.updateFeedbackReview(testValidFeedbackId);
const testResponse_Feedback = await request(app)
.post(`${apiUrl}/create`)
.send(testParam_dto);
const testResponse_FeedbackRating =
- await feedbackRatingService.getExtendedFeedbackRatingById(mockId.clients.valid[0]);
+ await feedbackRatingService.getExtendedFeedbackRatingById(testValidClientId);
expect(testResponse_Feedback.statusCode).toBe(200);
expect(testResponse_Feedback.body).toMatchObject(testResult);
@@ -230,13 +234,13 @@ describe('Integration-tests (repository), priority: entity Feedback', () => {
test('Repository process fn upsertInTa(), result: Exception on Feedback + Rollback', async () => {
const testParam_dto: FeedbackCreateDTO = {
- client_id: mockId.clients.valid[1],
- user_id: mockId.users.valid[0],
+ client_id: mockId.clients.valid[1] as ClientsId,
+ user_id: testValidUserId,
rating: 5,
term_accepted: true,
message: 'test-feedback-message-new-client[1]'
};
- const mockError = 'Feedback-upsert-mock-error';
+ const mockError = 'feedback-upsert-mock-error';
jest.spyOn(CommonUtils, 'logError').mockImplementation();
jest.spyOn(CommonUtils, "getTimestampUTC").mockReturnValue(testTimestamp);
@@ -247,15 +251,15 @@ describe('Integration-tests (repository), priority: entity Feedback', () => {
.rejects.toThrow(new DBQueryErrorException(mockError));
const testResponse_Feedback =
- await feedbackService.searchFeedbackEntriesByFilter({client_id: mockId.clients.valid[1]});
+ await feedbackService.searchFeedbackEntriesByFilter({client_id: mockId.clients.valid[1] as ClientsId});
expect(testResponse_Feedback).toBe(null); // rollback => no insert
})
test('Repository process fn upsertInTa(), result: Exception on FeedbackRating + Rollback', async () => {
const testParam_dto: FeedbackCreateDTO = {
- client_id: mockId.clients.valid[1],
- user_id: mockId.users.valid[0],
+ client_id: mockId.clients.valid[1] as ClientsId,
+ user_id: testValidUserId,
rating: 5,
term_accepted: true,
message: 'test-feedback-message-new-client[1]'
@@ -267,9 +271,9 @@ describe('Integration-tests (repository), priority: entity Feedback', () => {
await dbTestSetup.addTestData();
const testResponse_Feedback =
- await feedbackService.searchFeedbackEntriesByFilter({client_id: mockId.clients.valid[1]});
+ await feedbackService.searchFeedbackEntriesByFilter({client_id: mockId.clients.valid[1] as ClientsId});
const testResponse_FeedbackRating =
- await feedbackRatingService.getExtendedFeedbackRatingById(mockId.clients.valid[1]);
+ await feedbackRatingService.getExtendedFeedbackRatingById(mockId.clients.valid[1] as ClientsId);
await expect(() => feedbackService.createFeedback(testParam_dto))
.rejects.toThrow(new DBQueryErrorException(mockError));
@@ -278,9 +282,45 @@ describe('Integration-tests (repository), priority: entity Feedback', () => {
expect(testResponse_FeedbackRating).toBe(null);
})
+ test('Repository process fn upsertInTa(), result: Exception on `Blocked`', async () => {
+ const testParam_dto: FeedbackCreateDTO = {
+ client_id: mockId.clients.valid[1] as ClientsId,
+ user_id: testValidUserId,
+ rating: 3,
+ term_accepted: true,
+ message: 'test-feedback-message-new-client[1]'
+ };
+ const testFeedbackId = testValidFeedbackId;
+ const testResult: FeedbackResponseDTO | null = {
+ feedback_id: testFeedbackId,
+ client_id: testValidClientId,
+ user_id: testValidUserId,
+ rating: dbData_Feedback[2],
+ term_accepted: dbData_Feedback[3],
+ message: dbData_Feedback[4],
+ last_modified: testTimestamp,
+ created_on: testTimestamp,
+ blocked: true
+ };
+ const mockError = 'support-constraint-feedback';
+
+ jest.spyOn(CommonUtils, "getTimestampUTC").mockReturnValue(testTimestamp);
+ jest.spyOn(feedbackRepository, 'upsertInTa').mockResolvedValue(testResult);
+
+ await dbTestSetup.addTestData();
+ await expect(() => feedbackService.createFeedback(testParam_dto))
+ .rejects.toThrow(new DBConstraintErrorException(mockError));
+
+ const testCompareResult = await feedbackService.getFeedbackById(testFeedbackId);
+
+ expect(testCompareResult?.rating).not.toBe(testParam_dto.rating);
+ expect(testCompareResult?.message).not.toBe(testParam_dto.message);
+ expect(testCompareResult?.last_modified).toBe(testResult.last_modified);
+ })
+
test('Repository process fn updateReview(), result: "SUCCESS"', async () => {
const app = createTestApp([], router, apiUrl);
- const testParam_id = mockId.feedback.valid[0];
+ const testParam_id = testValidFeedbackId;
const mockTimestamp = '2026-01-01T14:00:08.000Z';
// Test without changing created_on this time to see how .spyOn works with multiple calls in process.
jest.spyOn(CommonUtils, "getTimestampUTC")
@@ -291,8 +331,8 @@ describe('Integration-tests (repository), priority: entity Feedback', () => {
const testResult: FeedbackResponseDTO = {
feedback_id: testParam_id,
- client_id: testValidClientsId,
- user_id: testValidUsersId,
+ client_id: testValidClientId,
+ user_id: testValidUserId,
rating: dbData_Feedback[2],
term_accepted: dbData_Feedback[3],
message: dbData_Feedback[4],
@@ -330,7 +370,7 @@ describe('Integration-tests (repository), priority: entity Feedback', () => {
describe('Route: GET/id/:id', () => {
test('Params: , validator: fn isInt() by value as string', async () => {
- const testParam_id = 'invalid-id';
+ const testParam_id = 'invalid-id-number';
const testError = structuredClone(mockError);
testError['value'] = testParam_id;
@@ -345,7 +385,7 @@ describe('Integration-tests (repository), priority: entity Feedback', () => {
describe('Route: PUT/update/review/:id', () => {
test('Params: , validator: fn isInt() by value as string', async () => {
- const testParam_id = 'invalid-id';
+ const testParam_id = 'invalid-id-number';
const testError = structuredClone(mockError);
testError['value'] = testParam_id;
@@ -380,7 +420,7 @@ describe('Integration-tests (repository), priority: entity Feedback', () => {
test('Params: , validator: fn isUUID() by invalid value', async () => {
let testParam_dto: FeedbackFilterDTO = {
- client_id: [mockId.clients.valid[0], 'invalid-id']
+ client_id: [testValidClientId, 'invalid-id'] as ClientsId[]
}
const testError = [{
type: 'field',
@@ -400,7 +440,7 @@ describe('Integration-tests (repository), priority: entity Feedback', () => {
test('Params: , validator: fn isUUID() by invalid value', async () => {
let testParam_dto: FeedbackFilterDTO = {
- user_id: ['invalid-id-0', mockId.clients.valid[0], 'invalid-id-1']
+ user_id: ['invalid-id-0', testValidUserId, 'invalid-id-1'] as UsersId[]
}
const testError = [
{
@@ -542,8 +582,8 @@ describe('Integration-tests (repository), priority: entity Feedback', () => {
let testData: Partial;
beforeEach(() => {
app = createTestApp([
- MockUtils.injectTestClientId(mockId.clients.valid[0]),
- MockUtils.injectTestUserId(mockId.users.valid[0])
+ MockUtils.injectTestClientId(testValidClientId),
+ MockUtils.injectTestUserId(testValidUserId)
], router, apiUrl);
testData = {
user_email: 'max.muster@test.com',
@@ -700,8 +740,8 @@ describe('Integration-tests (repository), priority: entity Feedback', () => {
let app: any;
beforeEach(() => {
app = createTestApp([
- MockUtils.injectTestClientId(mockId.clients.valid[0]),
- MockUtils.injectTestUserId(mockId.users.valid[0])
+ MockUtils.injectTestClientId(testValidClientId),
+ MockUtils.injectTestUserId(testValidUserId)
], router, apiUrl);
})
diff --git a/backend/tests/integration-tests/repositories/meta.integration.test.ts b/backend/tests/integration-tests/repositories/meta.integration.test.ts
index a845d29..a7f028c 100644
--- a/backend/tests/integration-tests/repositories/meta.integration.test.ts
+++ b/backend/tests/integration-tests/repositories/meta.integration.test.ts
@@ -1,4 +1,4 @@
-import { Maintenance, Meta } from './../../../src/repositories/interfaces/meta.entity.interface';
+import { Maintenance, Meta, MetaId } from './../../../src/repositories/interfaces/meta.entity.interface';
import { NextFunction, Request, Response } from "express";
import { DBTestSetup } from "../../db-container.setup";
import { runMigrations } from '../../db-migrations.setup';
@@ -12,6 +12,7 @@ import { CommonExceptionMessage } from '../../../src/utils/enums/common-exceptio
import { EnvMode } from '../../../src/utils/enums/env-mode.enum';
import { DemoMode } from '../../../src/utils/enums/demo-mode.enum';
import { default as mockId } from "../../mock-data/id.mock-data.json";
+import { secrets } from '../../../src/utils/secrets.utils';
jest.mock('../../../src/middleware/auth.admin.middleware', () => ({
authAdmin: jest.fn(() => (req: Request, res: Response, next: NextFunction) => next())
@@ -23,6 +24,8 @@ jest.mock('../../../src/middleware/maintenance.middleware', () => ({
import app from '../../../src/app';
jest.setTimeout(60000);
+
+const testValidMetaId = mockId.meta.valid[0] as MetaId;
const testTimestamp = '2025-01-01T14:00:01.000Z';
describe('Integration-tests (repository), priority: entity Meta', () => {
@@ -37,7 +40,7 @@ describe('Integration-tests (repository), priority: entity Meta', () => {
await runMigrations('meta.integration.test.ts');
apiUrl = '/api/v1/meta';
mockResult = {
- id: 1,
+ id: testValidMetaId,
app: 'support',
author: 'yqni13',
build_on: testTimestamp,
@@ -65,7 +68,7 @@ describe('Integration-tests (repository), priority: entity Meta', () => {
});
test('Repository process fn findById(), result: "SUCCESS"', async () => {
- const testParam_id = 1;
+ const testParam_id = testValidMetaId;
const testResult: Meta = {
id: testParam_id,
app: "support",
@@ -84,7 +87,7 @@ describe('Integration-tests (repository), priority: entity Meta', () => {
await dbTestSetup.addTestData();
const testResponse = await request(app)
- .get(`${apiUrl}/by-id/${testParam_id}`);
+ .get(`${apiUrl}/id/${testParam_id}`);
expect(testResponse.statusCode).toBe(200);
expect(testResponse.body).toMatchObject(testResult)
@@ -93,7 +96,7 @@ describe('Integration-tests (repository), priority: entity Meta', () => {
test('Repository process fn findByName(), result: "SUCCESS"', async () => {
const testParam_name = "support";
const testResult: Meta = {
- id: 1,
+ id: testValidMetaId,
app: testParam_name,
author: "yqni13",
build_on: testTimestamp,
@@ -110,7 +113,7 @@ describe('Integration-tests (repository), priority: entity Meta', () => {
await dbTestSetup.addTestData();
const testResponse = await request(app)
- .get(`${apiUrl}/by-name/${testParam_name}`);
+ .get(`${apiUrl}/name/${testParam_name}`);
expect(testResponse.statusCode).toBe(200);
expect(testResponse.body).toMatchObject(testResult)
@@ -118,7 +121,7 @@ describe('Integration-tests (repository), priority: entity Meta', () => {
test('Repository process fn findAll(), result: "SUCCESS"', async () => {
const testResult: Meta[] = [{
- id: 1,
+ id: testValidMetaId,
app: "support",
author: "yqni13",
build_on: testTimestamp,
@@ -142,7 +145,7 @@ describe('Integration-tests (repository), priority: entity Meta', () => {
})
test('Repository process fn update(), result: "SUCCESS"', async () => {
- const testParam_id = 1;
+ const testParam_id = testValidMetaId;
const testParam_dto: MetaUpdateDTO = {
app: 'support',
author: 'yqni13',
@@ -193,7 +196,7 @@ describe('Integration-tests (repository), priority: entity Meta', () => {
})
test('Repository process fn updateMaintenance(), result: "SUCCESS"', async () => {
- const testParam_id = mockId.meta.valid[0];
+ const testParam_id = testValidMetaId;
const testParam_data = { maintenance_mode: MaintenanceMode.E013 };
// Mock Utils generated timeStamp for easy comparison.
@@ -224,6 +227,8 @@ describe('Integration-tests (repository), priority: entity Meta', () => {
const testResult = { app_version: '0.0.1' };
+ jest.replaceProperty(secrets, 'APP_VERSION', '0.0.1');
+
await dbTestSetup.addTestData();
const testResponse = await request(app)
.post('/api/v1/test/demo')
@@ -254,7 +259,7 @@ describe('Integration-tests (repository), priority: entity Meta', () => {
describe('All routes, priority: express-validators, location: ', () => {
- describe('Route: GET/by-id:id', () => {
+ describe('Route: GET/id:id', () => {
test('Params: , validator: fn isInt() by string', async () => {
const testParam_id = 'invalid_test_id';
@@ -267,7 +272,7 @@ describe('Integration-tests (repository), priority: entity Meta', () => {
};
const testResponse = await request(app)
- .get(`${apiUrl}/by-id/${testParam_id}`);
+ .get(`${apiUrl}/id/${testParam_id}`);
expect(testResponse.statusCode).toBe(ErrorStatusCodes.InvalidPropertiesException);
expect(testResponse.body.headers.data).toEqual([testError]);
@@ -365,7 +370,7 @@ describe('Integration-tests (repository), priority: entity Meta', () => {
const testedParams = Object.keys(testData) as (keyof typeof testData)[];
test.each(testedParams)('Params: <%s>, validator: fn notEmpty() by undefined', async (invalidParam) => {
- const testParam_id = 1;
+ const testParam_id = testValidMetaId;
let mockParam_dto = structuredClone(testData);
delete mockParam_dto[invalidParam];
const testError = structuredClone(mockError);
@@ -397,7 +402,7 @@ describe('Integration-tests (repository), priority: entity Meta', () => {
describe('Route: PUT/info/:id', () => {
test('Params: , validator: fn requirePayload() by undefined', async () =>{
- const testParam_id = 1;
+ const testParam_id = testValidMetaId;
const testParam_dto = undefined;
const testError = structuredClone(mockError);
@@ -415,7 +420,7 @@ describe('Integration-tests (repository), priority: entity Meta', () => {
describe('Route: PUT/maintenance/:id', () => {
test('Params: , validator: fn requirePayload() by undefined', async () =>{
- const testParam_id = mockId.meta.valid[0];
+ const testParam_id = testValidMetaId;
const testParam_dto = undefined;
const testError = structuredClone(mockError);
diff --git a/backend/tests/integration-tests/repositories/rate-limits.integration.test.ts b/backend/tests/integration-tests/repositories/rate-limits.integration.test.ts
index 8471bf2..f6a60ec 100644
--- a/backend/tests/integration-tests/repositories/rate-limits.integration.test.ts
+++ b/backend/tests/integration-tests/repositories/rate-limits.integration.test.ts
@@ -12,11 +12,16 @@ import * as CommonUtils from '../../../src/utils/common.utils';
import rateLimitsService from "../../../src/services/rate-limits.service";
import demoLimitsService from "../../../src/services/demo-limits.service";
import { DemoLimitsCountDTO, DemoLimitsResponseDTO } from "../../../src/dtos/demo-limits.dto";
+import { ClientsId } from "../../../src/repositories/interfaces/clients.entity.interface";
+import { UsersId } from "../../../src/repositories/interfaces/users.entity.interface";
+import { RateLimitsId } from "../../../src/repositories/interfaces/rate-limits.entity.interface";
+import { DemoLimitsId } from "../../../src/repositories/interfaces/demo-limits.entity.interface";
jest.setTimeout(60000);
-const testValidClientsId = mockId.clients.valid[0];
-const testValidUsersId = mockId.users.valid[0];
+const testValidRateLimitId = mockId.rate_limits.valid[0] as RateLimitsId;
+const testValidClientId = mockId.clients.valid[0] as ClientsId;
+const testValidUserId = mockId.users.valid[0] as UsersId;
describe('Integration-tests (repository), priority: entity RateLimits', () => {
@@ -40,7 +45,7 @@ describe('Integration-tests (repository), priority: entity RateLimits', () => {
test('Repository process fn getRateLimitCount(), params: , result: "SUCCESS"', async () => {
const dto: RateLimitsCountDTO = {
- client_id: mockId.clients.valid[0],
+ client_id: testValidClientId,
day: '2025-01-01'
};
@@ -66,8 +71,8 @@ describe('Integration-tests (repository), priority: entity RateLimits', () => {
test('Repository process fn createRateLimit(), result: "SUCCESS"', async () => {
const testTimestamp = '2025-01-02T14:00:05.000Z';
const mockParam_dto: RateLimitsCreateDTO = {
- client_id: testValidClientsId,
- user_id: testValidUsersId
+ client_id: testValidClientId,
+ user_id: testValidUserId
};
const dateUTC = CommonUtils.getDateUTC(new Date(testTimestamp));
@@ -77,9 +82,9 @@ describe('Integration-tests (repository), priority: entity RateLimits', () => {
await dbTestSetup.addTestData();
const testResponse = await rateLimitsService.createRateLimit(mockParam_dto);
const mockResponse: RateLimitsResponseDTO = {
- rate_limit_id: 2,
- client_id: testValidClientsId,
- user_id: testValidUsersId,
+ rate_limit_id: mockId.rate_limits.new[0] as RateLimitsId,
+ client_id: testValidClientId,
+ user_id: testValidUserId,
day: '2025-01-02',
count: 1,
last_modified: testTimestamp
@@ -92,8 +97,8 @@ describe('Integration-tests (repository), priority: entity RateLimits', () => {
test('Repository process fn updateRateLimit(), result: "SUCCESS"', async () => {
const testTimestamp = '2025-01-01T22:22:22.000Z';
const dto: RateLimitsUpdateDTO = {
- client_id: testValidClientsId,
- user_id: testValidUsersId
+ client_id: testValidClientId,
+ user_id: testValidUserId
};
jest.spyOn(CommonUtils, 'getDateUTC').mockReturnValue(testTimestamp);
@@ -102,9 +107,9 @@ describe('Integration-tests (repository), priority: entity RateLimits', () => {
await dbTestSetup.addTestData();
const testResponse = await rateLimitsService.updateRateLimit(dto);
const mockResponse: RateLimitsResponseDTO = {
- rate_limit_id: 1,
- client_id: testValidClientsId,
- user_id: testValidUsersId,
+ rate_limit_id: testValidRateLimitId,
+ client_id: testValidClientId,
+ user_id: testValidUserId,
day: '2025-01-01',
count: 2,
last_modified: testTimestamp
@@ -117,8 +122,8 @@ describe('Integration-tests (repository), priority: entity RateLimits', () => {
test('Repository process fn updateRateLimit(), result: null', async () => {
const testTimestamp = '2025-01-02T22:22:22.000Z';
const dto: RateLimitsUpdateDTO = {
- client_id: testValidClientsId,
- user_id: testValidUsersId
+ client_id: testValidClientId,
+ user_id: testValidUserId
};
jest.spyOn(CommonUtils, 'getDateUTC').mockReturnValue(testTimestamp);
@@ -156,7 +161,7 @@ describe('Integration-tests (repository), priority: entity RateLimits', () => {
await dbTestSetup.addTestData();
const testResponse = await demoLimitsService.createDemoLimit();
const mockResponse: DemoLimitsResponseDTO = {
- demo_limit_id: 2,
+ demo_limit_id: 2 as DemoLimitsId,
day: '2025-01-02',
count: 1,
last_modified: testTimestamp
@@ -175,7 +180,7 @@ describe('Integration-tests (repository), priority: entity RateLimits', () => {
await dbTestSetup.addTestData();
const testResponse = await demoLimitsService.updateDemoLimit();
const mockResponse: DemoLimitsResponseDTO = {
- demo_limit_id: 1,
+ demo_limit_id: 1 as DemoLimitsId,
day: '2025-01-01',
count: 2,
last_modified: testTimestamp
diff --git a/backend/tests/integration-tests/repositories/tickets.integration.test.ts b/backend/tests/integration-tests/repositories/tickets.integration.test.ts
index e78d98e..2a1119d 100644
--- a/backend/tests/integration-tests/repositories/tickets.integration.test.ts
+++ b/backend/tests/integration-tests/repositories/tickets.integration.test.ts
@@ -21,9 +21,13 @@ import ticketsModel from "../../../src/models/tickets.model";
import { CloudService } from "../../../src/services/cloud.service";
import { TicketOption } from "../../../src/utils/enums/ticket-option.enum";
import { DeviceOption } from "../../../src/utils/enums/device-option.enum";
+import { UsersId } from "../../../src/repositories/interfaces/users.entity.interface";
+import { ClientsId } from "../../../src/repositories/interfaces/clients.entity.interface";
+import { TicketsId } from "../../../src/repositories/interfaces/tickets.entity.interface";
-const testValidClientsId = mockId.clients.valid[0];
-const testValidUsersId = mockId.users.valid[0];
+const testValidTicketId = mockId.tickets.valid[0] as TicketsId;
+const testValidClientId = mockId.clients.valid[0] as ClientsId;
+const testValidUserId = mockId.users.valid[0] as UsersId;
const testTimestamp = '2025-01-01T14:00:04.000Z';
jest.mock('../../../src/middleware/auth.admin.middleware', () => ({
@@ -31,13 +35,13 @@ jest.mock('../../../src/middleware/auth.admin.middleware', () => ({
}));
jest.mock('../../../src/middleware/auth.client.middleware', () => ({
authClient: jest.fn(() => (req: Request, res: Response, next: NextFunction) => {
- (req as any).apiClients = { client_id: testValidClientsId };
+ (req as any).apiClients = { client_id: testValidClientId };
next();
})
}));
jest.mock('../../../src/middleware/auth.user.middleware', () => ({
authUser: jest.fn(() => (req: Request, res: Response, next: NextFunction) => {
- (req as any).apiUsers = { user_id: testValidUsersId };
+ (req as any).apiUsers = { user_id: testValidUserId };
next();
})
}));
@@ -58,7 +62,7 @@ describe('Integration-tests (repository), priority: entity Tickets', () => {
let apiUrl: string;
const testClientsName = 'TESTCLIENT';
const testUsersEmail = 'max.mustermann@yqni13.com';
- const testNewParam_ticket_id = mockId.tickets.new[0];
+ const testNewParam_ticket_id = mockId.tickets.new[0] as TicketsId;
beforeAll(async () => {
dbTestSetup = new DBTestSetup();
await dbTestSetup.init();
@@ -79,12 +83,12 @@ describe('Integration-tests (repository), priority: entity Tickets', () => {
describe('Testing valid fn calls', () => {
test('Repository process fn findById(), result: "SUCCESS"', async () => {
- const testParam_id = mockId.tickets.valid[0];
+ const testParam_id = testValidTicketId;
const testResult: TicketsResponseExtendedDTO = {
ticket_id: testParam_id,
- client_id: testValidClientsId,
+ client_id: testValidClientId,
client_name: testClientsName,
- user_id: testValidUsersId,
+ user_id: testValidUserId,
user_email: testUsersEmail,
status: TicketStatus.ISSUED,
option: TicketOption.SUPPORT,
@@ -101,7 +105,7 @@ describe('Integration-tests (repository), priority: entity Tickets', () => {
await dbTestSetup.addTestData();
const testResponse = await request(app)
- .get(`${apiUrl}/by-id/${testParam_id}`);
+ .get(`${apiUrl}/id/${testParam_id}`);
expect(testResponse.statusCode).toBe(200);
expect(testResponse.body).toMatchObject(testResult);
@@ -110,9 +114,9 @@ describe('Integration-tests (repository), priority: entity Tickets', () => {
test('Repository process fn findAll(), result: "SUCCESS"', async () => {
const testResult: TicketsResponseDTO[] = [
{
- ticket_id: mockId.tickets.valid[0],
- client_id: testValidClientsId,
- user_id: testValidUsersId,
+ ticket_id: testValidTicketId,
+ client_id: testValidClientId,
+ user_id: testValidUserId,
status: TicketStatus.ISSUED,
option: TicketOption.SUPPORT,
title: 'test-title',
@@ -126,9 +130,9 @@ describe('Integration-tests (repository), priority: entity Tickets', () => {
created_on: testTimestamp
},
{
- ticket_id: mockId.tickets.valid[1],
- client_id: testValidClientsId,
- user_id: testValidUsersId,
+ ticket_id: mockId.tickets.valid[1] as TicketsId,
+ client_id: testValidClientId,
+ user_id: testValidUserId,
status: TicketStatus.ISSUED,
option: TicketOption.SUPPORT,
title: 'test-title',
@@ -152,7 +156,7 @@ describe('Integration-tests (repository), priority: entity Tickets', () => {
test('Repository process fn findByFilter(), params: result: null', async () => {
const testParam_dto: TicketsFilterDTO = {
- client_id: mockId.tickets.invalid[0]
+ client_id: mockId.clients.invalid[0] as ClientsId
};
const testResult: TicketsResponseDTO[] | null = null;
@@ -167,14 +171,14 @@ describe('Integration-tests (repository), priority: entity Tickets', () => {
test('Repository process fn findByFilter(), params: result: "SUCCESS"', async () => {
const testParam_dto: TicketsFilterDTO = {
- user_id: [testValidUsersId, mockId.tickets.invalid[0]],
+ user_id: [testValidUserId, mockId.users.invalid[0]] as UsersId[],
status: TicketStatus.ISSUED
};
const testResult: TicketsResponseDTO[] = [
{
- ticket_id: mockId.tickets.valid[0],
- client_id: testValidClientsId,
- user_id: testValidUsersId,
+ ticket_id: testValidTicketId,
+ client_id: testValidClientId,
+ user_id: testValidUserId,
status: TicketStatus.ISSUED,
option: TicketOption.SUPPORT,
title: 'test-title',
@@ -188,9 +192,9 @@ describe('Integration-tests (repository), priority: entity Tickets', () => {
created_on: testTimestamp
},
{
- ticket_id: mockId.tickets.valid[1],
- client_id: testValidClientsId,
- user_id: testValidUsersId,
+ ticket_id: mockId.tickets.valid[1] as TicketsId,
+ client_id: testValidClientId,
+ user_id: testValidUserId,
status: TicketStatus.ISSUED,
option: TicketOption.SUPPORT,
title: 'test-title',
@@ -221,9 +225,9 @@ describe('Integration-tests (repository), priority: entity Tickets', () => {
};
const testResult: TicketsResponseDTO[] = [
{
- ticket_id: mockId.tickets.valid[0],
- client_id: testValidClientsId,
- user_id: testValidUsersId,
+ ticket_id: testValidTicketId,
+ client_id: testValidClientId,
+ user_id: testValidUserId,
status: TicketStatus.ISSUED,
option: TicketOption.SUPPORT,
title: 'test-title',
@@ -237,9 +241,9 @@ describe('Integration-tests (repository), priority: entity Tickets', () => {
created_on: testTimestamp
},
{
- ticket_id: mockId.tickets.valid[1],
- client_id: testValidClientsId,
- user_id: testValidUsersId,
+ ticket_id: mockId.tickets.valid[1] as TicketsId,
+ client_id: testValidClientId,
+ user_id: testValidUserId,
status: TicketStatus.ISSUED,
option: TicketOption.SUPPORT,
title: 'test-title',
@@ -270,9 +274,9 @@ describe('Integration-tests (repository), priority: entity Tickets', () => {
};
const testResult: TicketsResponseDTO[] = [
{
- ticket_id: mockId.tickets.valid[0],
- client_id: testValidClientsId,
- user_id: testValidUsersId,
+ ticket_id: testValidTicketId,
+ client_id: testValidClientId,
+ user_id: testValidUserId,
status: TicketStatus.ISSUED,
option: TicketOption.SUPPORT,
title: 'test-title',
@@ -286,9 +290,9 @@ describe('Integration-tests (repository), priority: entity Tickets', () => {
created_on: testTimestamp
},
{
- ticket_id: mockId.tickets.valid[1],
- client_id: testValidClientsId,
- user_id: testValidUsersId,
+ ticket_id: mockId.tickets.valid[1] as TicketsId,
+ client_id: testValidClientId,
+ user_id: testValidUserId,
status: TicketStatus.ISSUED,
option: TicketOption.SUPPORT,
title: 'test-title',
@@ -319,9 +323,9 @@ describe('Integration-tests (repository), priority: entity Tickets', () => {
};
const testResult: TicketsResponseDTO[] = [
{
- ticket_id: mockId.tickets.valid[0],
- client_id: testValidClientsId,
- user_id: testValidUsersId,
+ ticket_id: testValidTicketId,
+ client_id: testValidClientId,
+ user_id: testValidUserId,
status: TicketStatus.ISSUED,
option: TicketOption.SUPPORT,
title: 'test-title',
@@ -335,9 +339,9 @@ describe('Integration-tests (repository), priority: entity Tickets', () => {
created_on: testTimestamp
},
{
- ticket_id: mockId.tickets.valid[1],
- client_id: testValidClientsId,
- user_id: testValidUsersId,
+ ticket_id: mockId.tickets.valid[1] as TicketsId,
+ client_id: testValidClientId,
+ user_id: testValidUserId,
status: TicketStatus.ISSUED,
option: TicketOption.SUPPORT,
title: 'test-title',
@@ -393,8 +397,8 @@ describe('Integration-tests (repository), priority: entity Tickets', () => {
const testResult: TicketsResponseDTO = {
ticket_id: testNewParam_ticket_id,
- client_id: testValidClientsId,
- user_id: testValidUsersId,
+ client_id: testValidClientId,
+ user_id: testValidUserId,
status: TicketStatus.ISSUED,
option: TicketOption.SUPPORT,
title: testParam_dto.title,
@@ -434,8 +438,8 @@ describe('Integration-tests (repository), priority: entity Tickets', () => {
const testResult: TicketsResponseDTO = {
ticket_id: testNewParam_ticket_id,
- client_id: testValidClientsId,
- user_id: testValidUsersId,
+ client_id: testValidClientId,
+ user_id: testValidUserId,
status: TicketStatus.ISSUED,
option: TicketOption.SUPPORT,
title: testParam_dto.title,
@@ -492,8 +496,8 @@ describe('Integration-tests (repository), priority: entity Tickets', () => {
const testResult: TicketsResponseDTO = {
ticket_id: testNewParam_ticket_id,
- client_id: testValidClientsId,
- user_id: testValidUsersId,
+ client_id: testValidClientId,
+ user_id: testValidUserId,
status: TicketStatus.ISSUED,
option: TicketOption.SUPPORT,
title: testParam_dto.title,
@@ -520,7 +524,7 @@ describe('Integration-tests (repository), priority: entity Tickets', () => {
})
test('Repository process fn update() without resource_paths, result: "SUCCESS"', async () => {
- const testParam_id = mockId.tickets.valid[1];
+ const testParam_id = mockId.tickets.valid[1] as TicketsId;
const testParam_dto: TicketsUpdateDTO = {
status: TicketStatus.ACTIVE,
option: TicketOption.SUPPORT,
@@ -541,8 +545,8 @@ describe('Integration-tests (repository), priority: entity Tickets', () => {
info_os: 'Android 15',
info_device: DeviceOption.MOBILE,
ticket_id: testParam_id,
- client_id: testValidClientsId,
- user_id: testValidUsersId,
+ client_id: testValidClientId,
+ user_id: testValidUserId,
last_modified: mockTimestampNoPaths,
created_on: mockTimestampNoPaths
};
@@ -557,7 +561,7 @@ describe('Integration-tests (repository), priority: entity Tickets', () => {
})
test('Repository process fn update() with resource_paths, result: "SUCCESS"', async () => {
- const testParam_id = mockId.tickets.valid[0];
+ const testParam_id = testValidTicketId;
const testParam_dto: TicketsUpdateDTO = {
status: TicketStatus.ACTIVE,
option: TicketOption.BUG,
@@ -578,8 +582,8 @@ describe('Integration-tests (repository), priority: entity Tickets', () => {
info_os: 'Windows 11',
info_device: DeviceOption.COMPUTER,
ticket_id: testParam_id,
- client_id: testValidClientsId,
- user_id: testValidUsersId,
+ client_id: testValidClientId,
+ user_id: testValidUserId,
last_modified: testTimestamp,
created_on: testTimestamp
};
@@ -607,7 +611,7 @@ describe('Integration-tests (repository), priority: entity Tickets', () => {
})
test('Repository process fn delete() with existing data for resource_paths, result: "SUCCESS"', async () => {
- const testParam_id = mockId.tickets.valid[0];
+ const testParam_id = testValidTicketId;
const testResult = true;
jest.spyOn(ticketsModel, 'isPermittedToDelete').mockReturnValue(true);
@@ -769,15 +773,15 @@ describe('Integration-tests (repository), priority: entity Tickets', () => {
};
})
- describe('Route: GET/by-id/:id', () => {
+ describe('Route: GET/id/:id', () => {
test('Params: , validator: fn isUUID() by invalid id', async () => {
- const testParam_id = 'invalid-id';
+ const testParam_id = 'invalid-UUID';
const testError = structuredClone(mockError);
testError['value'] = testParam_id;
const testResponse = await request(app)
- .get(`${apiUrl}/by-id/${testParam_id}`);
+ .get(`${apiUrl}/id/${testParam_id}`);
expect(testResponse.statusCode).toBe(ErrorStatusCodes.InvalidPropertiesException);
expect(testResponse.body.headers.data).toEqual([testError]);
@@ -787,7 +791,7 @@ describe('Integration-tests (repository), priority: entity Tickets', () => {
describe('Route: PUT/update/:id', () => {
test('Params: , validator: fn isUUID() by invalid id', async () => {
- const testParam_id = 'invalid-id';
+ const testParam_id = 'invalid-UUID';
const testParam_dto: TicketsUpdateDTO = {
status: TicketStatus.ACTIVE,
option: TicketOption.SUPPORT,
@@ -810,7 +814,7 @@ describe('Integration-tests (repository), priority: entity Tickets', () => {
describe('Route: DELETE/delete/:id', () => {
test('Params: , validator: fn isUUID() by invalid id', async () => {
- const testParam_id = 'invalid-id';
+ const testParam_id = 'invalid-UUID';
const testError = structuredClone(mockError);
testError['value'] = testParam_id;
@@ -840,7 +844,7 @@ describe('Integration-tests (repository), priority: entity Tickets', () => {
test('Params: , validator: fn isUUID() by invalid value', async () => {
let testParam_dto: TicketsFilterDTO = {
- client_id: [mockId.clients.valid[0], 'invalid-id']
+ client_id: [testValidClientId, 'invalid-id'] as ClientsId[]
}
const testError = [{
type: 'field',
@@ -860,7 +864,7 @@ describe('Integration-tests (repository), priority: entity Tickets', () => {
test('Params: , validator: fn isUUID() by invalid value', async () => {
let testParam_dto: TicketsFilterDTO = {
- user_id: ['invalid-id-0', mockId.clients.valid[0], 'invalid-id-1']
+ user_id: ['invalid-id-0', testValidUserId, 'invalid-id-1'] as UsersId[]
}
const testError = [
{
@@ -1036,10 +1040,10 @@ describe('Integration-tests (repository), priority: entity Tickets', () => {
describe('Route: PUT/update/:id', () => {
- let testParam_id: string;
+ let testParam_id: TicketsId;
let testData: Partial;
beforeEach(() => {
- testParam_id = mockId.tickets.invalid[0];
+ testParam_id = mockId.tickets.invalid[0] as TicketsId;
testData = {
status: TicketStatus.ACTIVE,
option: TicketOption.SUPPORT,
@@ -1218,7 +1222,7 @@ describe('Integration-tests (repository), priority: entity Tickets', () => {
describe('Route: PUT/update/:id', () => {
test('Params: , validator: fn hasBodyPayload() by undefined', async () =>{
- const testParam_id = mockId.tickets.valid[0];
+ const testParam_id = testValidTicketId;
const testParam_dto = undefined;
jest.spyOn(CommonUtils, 'logError').mockImplementation();
diff --git a/backend/tests/integration-tests/repositories/users.integration.test.ts b/backend/tests/integration-tests/repositories/users.integration.test.ts
index 718e666..2966c96 100644
--- a/backend/tests/integration-tests/repositories/users.integration.test.ts
+++ b/backend/tests/integration-tests/repositories/users.integration.test.ts
@@ -6,7 +6,7 @@ import { ErrorStatusCodes } from '../../../src/utils/errorStatusCodes.utils';
import { DBTestSetup } from "../../db-container.setup";
import { runMigrations } from '../../db-migrations.setup';
import { UsersUpdateDTO, UsersFilterDTO, UsersResponseDTO, UsersCreateDTO, UsersFlagUpdateDTO } from "../../../src/dtos/users.dto";
-import { Users } from "../../../src/repositories/interfaces/users.entity.interface";
+import { Users, UsersId } from "../../../src/repositories/interfaces/users.entity.interface";
import { UserStatus } from "../../../src/utils/enums/user-status.enum";
import { Flag } from "../../../src/utils/enums/flag.enum";
import { default as mockId } from "../../mock-data/id.mock-data.json";
@@ -23,6 +23,8 @@ jest.mock('../../../src/middleware/maintenance.middleware', () => ({
import app from '../../../src/app';
jest.setTimeout(60000);
+
+const testValidUserId = mockId.users.valid[0] as UsersId;
const testTimestamp = '2025-01-01T14:00:03.000Z';
describe('Integration-tests (repository), priority: entity Users', () => {
@@ -48,7 +50,7 @@ describe('Integration-tests (repository), priority: entity Users', () => {
describe('Testing valid fn calls', () => {
test('Repository process fn findById(), result: "SUCCESS"', async () => {
- const testParam_id = mockId.users.valid[0];
+ const testParam_id = testValidUserId;
const testResult: UsersResponseDTO = {
user_id: testParam_id,
email: 'max.mustermann@yqni13.com',
@@ -60,7 +62,7 @@ describe('Integration-tests (repository), priority: entity Users', () => {
await dbTestSetup.addTestData();
const testResponse = await request(app)
- .get(`${apiUrl}/by-id/${testParam_id}`);
+ .get(`${apiUrl}/id/${testParam_id}`);
expect(testResponse.statusCode).toBe(200);
expect(testResponse.body).toMatchObject(testResult);
@@ -69,7 +71,7 @@ describe('Integration-tests (repository), priority: entity Users', () => {
test('Repository process fn findByEmail(), result: "SUCCESS"', async () => {
const testParam_email = 'max.mustermann@yqni13.com';
const testResult: UsersResponseDTO = {
- user_id: mockId.users.valid[0],
+ user_id: testValidUserId,
email: testParam_email,
status: UserStatus.ACTIVE,
flag: null,
@@ -79,17 +81,17 @@ describe('Integration-tests (repository), priority: entity Users', () => {
await dbTestSetup.addTestData();
const testResponse = await request(app)
- .get(`${apiUrl}/by-email/${testParam_email}`);
+ .get(`${apiUrl}/email/${testParam_email}`);
expect(testResponse.statusCode).toBe(200);
expect(testResponse.body).toMatchObject(testResult);
})
test('Repository process fn findAll(), result: "SUCCESS"', async () => {
- const testParam_id = mockId.users.valid[0];
+ const testParam_id = testValidUserId;
const testResult: Users[] = [
{
- user_id: mockId.users.valid[1],
+ user_id: mockId.users.valid[1] as UsersId,
email: 'aurora.otsuki@yqni13.com',
status: UserStatus.ACTIVE,
flag: null,
@@ -136,7 +138,7 @@ describe('Integration-tests (repository), priority: entity Users', () => {
status: UserStatus.ACTIVE
};
const testResult: UsersResponseDTO[] = [{
- user_id: mockId.users.valid[0],
+ user_id: testValidUserId,
email: 'max.mustermann@yqni13.com',
status: UserStatus.ACTIVE,
flag: null,
@@ -161,7 +163,7 @@ describe('Integration-tests (repository), priority: entity Users', () => {
last_modified: ['2024-12-01T10:00:00.000Z', '2025-12-01T14:00:00.000Z']
};
const testResult: UsersResponseDTO[] = [{
- user_id: mockId.users.valid[0],
+ user_id: testValidUserId,
email: 'max.mustermann@yqni13.com',
status: UserStatus.ACTIVE,
flag: null,
@@ -181,7 +183,7 @@ describe('Integration-tests (repository), priority: entity Users', () => {
})
test('Repository process fn create(), result: "SUCCESS"', async () => {
- const testParam_id = mockId.users.new[0];
+ const testParam_id = mockId.users.new[0] as UsersId;
const testParam_dto: UsersCreateDTO = {
email: 'new-user@test.com'
};
@@ -208,7 +210,7 @@ describe('Integration-tests (repository), priority: entity Users', () => {
})
test('Repository process fn update(), result: "SUCCESS"', async () => {
- const testParam_id = mockId.users.valid[0];
+ const testParam_id = testValidUserId;
const testParam_dto: Partial = {
email: 'user@test.com',
status: UserStatus.ACTIVE,
@@ -234,7 +236,7 @@ describe('Integration-tests (repository), priority: entity Users', () => {
})
test('Repository process fn updateFlag(), result: "SUCCESS"', async () => {
- const testParam_id = mockId.users.valid[0];
+ const testParam_id = testValidUserId;
const testParam_dto: UsersFlagUpdateDTO = {
flag: Flag.WARNING
};
@@ -263,10 +265,10 @@ describe('Integration-tests (repository), priority: entity Users', () => {
describe('All routes, priority: express-validators, location: ', () => {
- describe('Route: GET/by-id/:id', () => {
+ describe('Route: GET/id/:id', () => {
test('Params: , validator: fn isUUID() by invalid id', async () => {
- const testParam_id = 'invalid-id';
+ const testParam_id = 'invalid-UUID';
const testError = {
type: 'field',
value: testParam_id,
@@ -276,7 +278,7 @@ describe('Integration-tests (repository), priority: entity Users', () => {
}
const testResponse = await request(app)
- .get(`${apiUrl}/by-id/${testParam_id}`);
+ .get(`${apiUrl}/id/${testParam_id}`);
expect(testResponse.statusCode).toBe(ErrorStatusCodes.InvalidPropertiesException);
expect(testResponse.body.headers.data).toEqual([testError]);
@@ -286,7 +288,7 @@ describe('Integration-tests (repository), priority: entity Users', () => {
describe('Route: PUT/update/:id', () => {
test('Params: , validator: fn isUUID() by invalid id', async () => {
- const testParam_id = 'invalid-id';
+ const testParam_id = 'invalid-UUID';
const testParam_dto: UsersUpdateDTO = {
email: 'new-user@test.com',
status: UserStatus.ACTIVE,
@@ -333,7 +335,7 @@ describe('Integration-tests (repository), priority: entity Users', () => {
const testedParams = Object.keys(testData) as (keyof typeof testData)[];
test.each(testedParams)('Params: <%s>, validator: fn notEmpty() by undefined', async (invalidParam) => {
- const testParam_id = mockId.users.valid[0];
+ const testParam_id = testValidUserId;
let mockParam_dto = structuredClone(testData);
delete mockParam_dto[invalidParam];
@@ -410,7 +412,7 @@ describe('Integration-tests (repository), priority: entity Users', () => {
describe('Route: PUT/update/:id', () => {
test('Params: , validator: fn requirePayload() by undefined', async () =>{
- const testParam_id = mockId.users.valid[0];
+ const testParam_id = testValidUserId;
const testParam_dto = undefined;
const testError = structuredClone(mockError);
diff --git a/backend/tests/mock-data/id.mock-data.json b/backend/tests/mock-data/id.mock-data.json
index 8947320..456f015 100644
--- a/backend/tests/mock-data/id.mock-data.json
+++ b/backend/tests/mock-data/id.mock-data.json
@@ -1,7 +1,8 @@
{
"clients": {
"invalid": [
- "c34ad3f7-e263-42a0-bc09-b2d9dd2598c6"
+ "c34ad3f7-e263-42a0-bc09-b2d9dd2598c6",
+ "3fd1c826-4c30-42f1-becf-5389575f0d2e"
],
"new": [
"56a7a3f5-e2f4-4040-bb00-f194f8853d62"
@@ -40,17 +41,18 @@
0
],
"new": [
- 3,
+ 2,
4
],
"valid": [
1,
- 2
+ 3
]
},
"tickets": {
"invalid": [
- "b284d72d-1000-412c-8a7f-bbc9f9b5113d"
+ "b284d72d-1000-412c-8a7f-bbc9f9b5113d",
+ "e3dedcd2-bab4-4f05-b592-abdceca708ac"
],
"new": [
"6404a514-602b-43de-b4eb-919c83222645"
@@ -62,7 +64,8 @@
},
"users": {
"invalid": [
- "3c11e85a-5377-4b93-8064-e07e27c643ab"
+ "3c11e85a-5377-4b93-8064-e07e27c643ab",
+ "fda3c5e1-3b09-4c49-8f0d-d495a2503a43"
],
"new": [
"73b29215-fb40-4b40-904a-2dfe4a448426"
diff --git a/backend/tests/unit-tests/middleware/auth.admin.middleware.test.ts b/backend/tests/unit-tests/middleware/auth.admin.middleware.test.ts
index 2843c74..dd7ba37 100644
--- a/backend/tests/unit-tests/middleware/auth.admin.middleware.test.ts
+++ b/backend/tests/unit-tests/middleware/auth.admin.middleware.test.ts
@@ -15,7 +15,7 @@ describe('Unit-tests (middleware), priority: fn authAdmin()', () => {
describe('Testing valid fn calls', () => {
test('Verfiy admin, params: valid ', async () => {
- const mockApiKey = secrets.ADMIN_API.trim();
+ const mockApiKey = secrets.ADMIN_KEY.trim();
req.header.mockReturnValue(mockApiKey);
// middleware == factory fn returning express fn => fn(req, res, next)
diff --git a/backend/tests/unit-tests/middleware/auth.client.middleware.test.ts b/backend/tests/unit-tests/middleware/auth.client.middleware.test.ts
index 27c0029..f90c782 100644
--- a/backend/tests/unit-tests/middleware/auth.client.middleware.test.ts
+++ b/backend/tests/unit-tests/middleware/auth.client.middleware.test.ts
@@ -1,5 +1,5 @@
import { ForbiddenApiKeyException, MissingApiKeyException } from './../../../src/utils/exceptions/auth.exception';
-import { Clients } from "../../../src/repositories/interfaces/clients.entity.interface";
+import { Clients, ClientsId } from "../../../src/repositories/interfaces/clients.entity.interface";
import clientsService from "../../../src/services/clients.service";
import { default as mockId } from "../../mock-data/id.mock-data.json";
import { secrets } from "../../../src/utils/secrets.utils";
@@ -11,6 +11,7 @@ import { ErrorStatusCodes } from "../../../src/utils/errorStatusCodes.utils";
describe('Unit-tests (middleware), priority: fn authClient()', () => {
+ const mockValidClientId = mockId.clients.valid[0] as ClientsId;
const mockTimestamp = '2025-01-01T14:00:00.000Z';
const req: any = { header: jest.fn() };
const res: any = {};
@@ -24,7 +25,7 @@ describe('Unit-tests (middleware), priority: fn authClient()', () => {
test('Verfiy client, params: valid ', async () => {
const mockApiKey = 'valid_api_key';
const mockClient: Clients = {
- client_id: mockId.clients.valid[0],
+ client_id: mockValidClientId,
name: 'valid_clients_test_name',
api_key_hash: secrets.TEST_APIKEY_HASH,
status: ApiKeyStatus.ACTIVE,
diff --git a/backend/tests/unit-tests/middleware/auth.user.middleware.test.ts b/backend/tests/unit-tests/middleware/auth.user.middleware.test.ts
index dea99fd..bbd6020 100644
--- a/backend/tests/unit-tests/middleware/auth.user.middleware.test.ts
+++ b/backend/tests/unit-tests/middleware/auth.user.middleware.test.ts
@@ -1,7 +1,7 @@
import { default as mockId } from "../../mock-data/id.mock-data.json";
import * as CommonUtils from "../../../src/utils/common.utils";
import { authUser } from "../../../src/middleware/auth.user.middleware";
-import { Users } from "../../../src/repositories/interfaces/users.entity.interface";
+import { Users, UsersId } from "../../../src/repositories/interfaces/users.entity.interface";
import { UserStatus } from "../../../src/utils/enums/user-status.enum";
import usersService from "../../../src/services/users.service";
import { UsersResponseDTO } from "../../../src/dtos/users.dto";
@@ -10,6 +10,7 @@ import { Flag } from "../../../src/utils/enums/flag.enum";
describe('Unit-tests (middleware), priority: fn authUser()', () => {
+ const mockValidUserId = mockId.users.valid[0] as UsersId;
const mockTimestamp = '2025-01-01T14:00:00.000Z';
const res: any = {};
const next = jest.fn();
@@ -22,7 +23,7 @@ describe('Unit-tests (middleware), priority: fn authUser()', () => {
test('Verfiy user, params: valid for existing user', async () => {
const mockUserEmail = 'valid_user@test.com';
const mockUser: Users = {
- user_id: mockId.users.valid[0],
+ user_id: mockValidUserId,
email: mockUserEmail,
status: UserStatus.ACTIVE,
flag: null,
@@ -47,7 +48,7 @@ describe('Unit-tests (middleware), priority: fn authUser()', () => {
const mockUserEmail = 'non-existing-user@test.com';
const mockUser: Users | null = null;
const mockNewUser: UsersResponseDTO = {
- user_id: mockId.users.valid[0],
+ user_id: mockValidUserId,
email: mockUserEmail,
status: UserStatus.ACTIVE,
flag: null,
@@ -75,7 +76,7 @@ describe('Unit-tests (middleware), priority: fn authUser()', () => {
test('Verify user, error: InvalidUsersException', async () => {
const mockUserEmail = 'valid-user@test.com';
const mockUser: Users = {
- user_id: mockId.users.valid[0],
+ user_id: mockValidUserId,
email: mockUserEmail,
status: UserStatus.ACTIVE,
flag: Flag.ERROR,
@@ -99,7 +100,7 @@ describe('Unit-tests (middleware), priority: fn authUser()', () => {
test('Verify user, error: BlockedUsersException', async () => {
const mockUserEmail = 'valid-user@test.com';
const mockUser: Users = {
- user_id: mockId.users.valid[0],
+ user_id: mockValidUserId,
email: mockUserEmail,
status: UserStatus.BLACKLISTED,
flag: Flag.ERROR,
diff --git a/backend/tests/unit-tests/middleware/engines.middleware.test.ts b/backend/tests/unit-tests/middleware/engines.middleware.test.ts
index 957dafb..ec9847a 100644
--- a/backend/tests/unit-tests/middleware/engines.middleware.test.ts
+++ b/backend/tests/unit-tests/middleware/engines.middleware.test.ts
@@ -8,23 +8,24 @@ import demoLimitsService from "../../../src/services/demo-limits.service";
import rateLimitsService from "../../../src/services/rate-limits.service";
import { Violation } from "../../../src/utils/enums/violations.enum";
import { default as mockId } from "../../mock-data/id.mock-data.json";
+import { ClientsId } from "../../../src/repositories/interfaces/clients.entity.interface";
+import { UsersId } from "../../../src/repositories/interfaces/users.entity.interface";
// Ensure correct type by converting secret to number via unary + operator.
import { secrets } from "../../../src/utils/secrets.utils";
describe('Unit-tests (middleware), priority: class RateLimitsEngine', () => {
+ const mockValidClientId = mockId.clients.valid[0] as ClientsId;
+ const mockValidUserId = mockId.users.valid[0] as UsersId;
+
describe('Testing valid fn calls', () => {
- let mockValidClientsId: string;
- let mockValidUsersId: string;
let mockParam_data: RateLimitsData;
beforeEach(() => {
- mockValidClientsId = mockId.clients.valid[0],
- mockValidUsersId = mockId.users.valid[0];
mockParam_data = {
- client_id: mockValidClientsId,
- user_id: mockValidUsersId
+ client_id: mockValidClientId,
+ user_id: mockValidUserId
};
});
@@ -57,7 +58,7 @@ describe('Unit-tests (middleware), priority: class RateLimitsEngine', () => {
retryAfter: '2025-01-02T00.00.01.000Z',
penalty: {
type: Violation.CLIENTSFLAG,
- id: mockValidClientsId,
+ id: mockValidClientId,
penaltyValue: null
}
};
@@ -89,7 +90,7 @@ describe('Unit-tests (middleware), priority: class RateLimitsEngine', () => {
const engine = new RateLimitsEngine([rule], count);
const mockResponse = null;
- const testFn = await engine.process({ client_id: 'demo', user_id: 'demo' });
+ const testFn = await engine.process({ client_id: 'demo' as ClientsId, user_id: 'demo' as UsersId });
expect(testFn).toBe(mockResponse);
})
@@ -107,7 +108,7 @@ describe('Unit-tests (middleware), priority: class RateLimitsEngine', () => {
const engine = new RateLimitsEngine([rule], count);
const mockResponse = response;
- const testFn = await engine.process({ client_id: 'demo', user_id: 'demo' });
+ const testFn = await engine.process({ client_id: 'demo' as ClientsId, user_id: 'demo' as UsersId });
expect(testFn).toBe(mockResponse);
})
diff --git a/backend/tests/unit-tests/middleware/maintenance.middleware.test.ts b/backend/tests/unit-tests/middleware/maintenance.middleware.test.ts
index 29e2a5b..33cdb21 100644
--- a/backend/tests/unit-tests/middleware/maintenance.middleware.test.ts
+++ b/backend/tests/unit-tests/middleware/maintenance.middleware.test.ts
@@ -6,9 +6,12 @@ import { MaintenanceMode } from '../../../src/utils/enums/maintenance-mode.enum'
import { ErrorStatusCodes } from '../../../src/utils/errorStatusCodes.utils';
import { maintain, updateTrafficError } from '../../../src/middleware/maintenance.middleware';
import { default as mockId } from "../../mock-data/id.mock-data.json";
+import { MetaId } from '../../../src/repositories/interfaces/meta.entity.interface';
describe('Unit-tests (middleware), priority: synonym MaintenanceMiddleware', () => {
+ const mockValidMetaId = mockId.meta.valid[0] as MetaId;
+
describe('Middleware tests, priority: fn maintain()', () => {
const mockTimestamp = '2025-01-01T14:00:00.000Z';
@@ -18,7 +21,7 @@ describe('Unit-tests (middleware), priority: synonym MaintenanceMiddleware', ()
let mockMaintenanceResponse: MaintenanceResponseDTO | null;
beforeEach(() => {
mockMaintenanceResponse = {
- id: mockId.meta.valid[0],
+ id: mockValidMetaId,
app: 'support',
build_on: mockTimestamp,
maintenance_mode: MaintenanceMode.A000,
@@ -129,7 +132,7 @@ describe('Unit-tests (middleware), priority: synonym MaintenanceMiddleware', ()
let mockMaintenanceResponse: MaintenanceResponseDTO;
beforeEach(() => {
mockMaintenanceResponse = {
- id: 1,
+ id: mockValidMetaId,
app: 'support',
build_on: mockTimestamp,
maintenance_mode: MaintenanceMode.T011,
diff --git a/backend/tests/unit-tests/middleware/rules/rate-limits.rules.middleware.test.ts b/backend/tests/unit-tests/middleware/rules/rate-limits.rules.middleware.test.ts
index a41a50f..3c1b42b 100644
--- a/backend/tests/unit-tests/middleware/rules/rate-limits.rules.middleware.test.ts
+++ b/backend/tests/unit-tests/middleware/rules/rate-limits.rules.middleware.test.ts
@@ -9,6 +9,7 @@ import {
import { TicketsResponseDTO } from "../../../../src/dtos/tickets.dto";
import { RateLimitsData, RateLimitsResponse } from "../../../../src/middleware/interfaces/rate-limits.interface.middleware";
import * as CommonUtils from "../../../../src/utils/common.utils";
+import { default as mockId } from "../../../mock-data/id.mock-data.json";
import ticketsService from "../../../../src/services/tickets.service";
import { TicketStatus } from "../../../../src/utils/enums/ticket-status.enum";
import rateLimitsService from "../../../../src/services/rate-limits.service";
@@ -16,41 +17,48 @@ import demoLimitsService from "../../../../src/services/demo-limits.service";
import { Violation } from "../../../../src/utils/enums/violations.enum";
import { MaintenanceMode } from "../../../../src/utils/enums/maintenance-mode.enum";
import { TicketOption } from "../../../../src/utils/enums/ticket-option.enum";
+import { ClientsId } from "../../../../src/repositories/interfaces/clients.entity.interface";
+import { UsersId } from "../../../../src/repositories/interfaces/users.entity.interface";
+import { TicketsId } from "../../../../src/repositories/interfaces/tickets.entity.interface";
// Ensure correct type by converting secret to number via unary + operator.
import { secrets } from "../../../../src/utils/secrets.utils"
+import { MetaId } from "../../../../src/repositories/interfaces/meta.entity.interface";
describe('Unit-tests (middleware), priority: implementation RateLimitsRule', () => {
- const mockValidClientsId = 'valid_clients_test_id';
- const mockValidUsersId = 'valid_users_test_id';
+ const mockValidTicketId = mockId.tickets.valid[0] as TicketsId;
+ const mockValidClientId = mockId.clients.valid[0] as ClientsId;
+ const mockValidUserId = mockId.users.valid[0] as UsersId;
let mockRetryAfter: string;
let mockParam_data: RateLimitsData;
let mockBurstContext: TicketsResponseDTO[];
beforeEach(() => {
mockRetryAfter = '2025-01-02T00:00:01.000Z';
mockParam_data = {
- client_id: mockValidClientsId,
- user_id: mockValidUsersId
+ client_id: mockValidClientId,
+ user_id: mockValidUserId
};
mockBurstContext = [
{
- ticket_id: 'valid_tickets_test_id_0',
- client_id: mockValidClientsId,
- user_id: mockValidUsersId,
+ ticket_id: mockValidTicketId,
+ client_id: mockValidClientId,
+ user_id: mockValidUserId,
status: TicketStatus.ISSUED,
option: TicketOption.SUPPORT,
+ title: 'test_title_0',
message: 'test_message_0',
flag: null,
last_modified: '2025-01-01T14:00:04.000Z',
created_on: '2025-01-01T14:00:04.000Z'
},
{
- ticket_id: 'valid_tickets_test_id_1',
- client_id: mockValidClientsId,
- user_id: mockValidUsersId,
+ ticket_id: 'valid_tickets_test_id_1' as TicketsId,
+ client_id: mockValidClientId,
+ user_id: mockValidUserId,
status: TicketStatus.ISSUED,
option: TicketOption.SUPPORT,
+ title: 'test_title_1',
message: 'test_message_1',
flag: null,
last_modified: '2025-01-01T14:00:11.000Z',
@@ -81,11 +89,12 @@ describe('Unit-tests (middleware), priority: implementation RateLimitsRule', ()
const ruleCBL = new ClientsBurstLimitRule(3);
const mockContext = structuredClone(mockBurstContext);
mockContext.push({
- ticket_id: 'valid_tickets_test_id_2',
- client_id: mockValidClientsId,
- user_id: mockValidUsersId,
+ ticket_id: 'valid_tickets_test_id_2' as TicketsId,
+ client_id: mockValidClientId,
+ user_id: mockValidUserId,
status: TicketStatus.ISSUED,
option: TicketOption.SUPPORT,
+ title: 'test_title_2',
message: 'test_message_2',
flag: null,
last_modified: '2025-01-01T14:00:23.000Z',
@@ -128,11 +137,12 @@ describe('Unit-tests (middleware), priority: implementation RateLimitsRule', ()
const ruleUBL = new UsersBurstLimitRule(3);
const mockContext = structuredClone(mockBurstContext);
mockContext.push({
- ticket_id: 'valid_tickets_test_id_2',
- client_id: mockValidClientsId,
- user_id: mockValidUsersId,
+ ticket_id: 'valid_tickets_test_id_2' as TicketsId,
+ client_id: mockValidClientId,
+ user_id: mockValidUserId,
status: TicketStatus.ISSUED,
option: TicketOption.SUPPORT,
+ title: 'test_title_2',
message: 'test_message_2',
flag: null,
last_modified: '2025-01-01T14:00:23.000Z',
@@ -196,7 +206,7 @@ describe('Unit-tests (middleware), priority: implementation RateLimitsRule', ()
retryAfter: mockRetryAfter,
penalty: {
type: Violation.CLIENTSFLAG,
- id: 'valid_clients_test_id',
+ id: mockValidClientId,
penaltyValue: null
}
};
@@ -250,7 +260,7 @@ describe('Unit-tests (middleware), priority: implementation RateLimitsRule', ()
retryAfter: mockRetryAfter,
penalty: {
type: Violation.USERSFLAG,
- id: 'valid_users_test_id',
+ id: mockValidUserId,
penaltyValue: null
}
};
@@ -302,7 +312,7 @@ describe('Unit-tests (middleware), priority: implementation RateLimitsRule', ()
retryAfter: mockRetryAfter,
penalty: {
type: Violation.MAINTENANCE_TRAFFIC,
- id: 1,
+ id: mockId.meta.valid[0] as MetaId,
penaltyValue: MaintenanceMode.T011
}
};
diff --git a/backend/tests/unit-tests/models/clients.models.test.ts b/backend/tests/unit-tests/models/clients.models.test.ts
index ecd6f19..0abe852 100644
--- a/backend/tests/unit-tests/models/clients.models.test.ts
+++ b/backend/tests/unit-tests/models/clients.models.test.ts
@@ -5,13 +5,14 @@ import {
import * as CommonUtils from "../../../src/utils/common.utils";
import { default as mockId } from "../../mock-data/id.mock-data.json";
import clientsModel from "../../../src/models/clients.model";
-import { Clients } from "../../../src/repositories/interfaces/clients.entity.interface";
+import { Clients, ClientsId } from "../../../src/repositories/interfaces/clients.entity.interface";
import { ApiKeyStatus } from "../../../src/utils/enums/api-key-status.enum";
-const mockVar_apiKey = clientsModel._generateApiKeyObj();
+const mockValidClientId = mockId.clients.valid[0] as ClientsId;
+const mockVar_apiKey = (clientsModel as any).generateApiKeyObj();
const mockTimestamp = '2025-01-01T14:00:02.000Z';
let mockData: Clients = {
- client_id: mockId.clients.valid[0],
+ client_id: mockValidClientId,
name: 'testclient',
api_key_hash: mockVar_apiKey.keyHash,
status: ApiKeyStatus.ACTIVE,
@@ -23,14 +24,14 @@ let mockData: Clients = {
describe('Unit-tests (model), priority: entity Clients', () => {
- describe('Priority: fn mapToCreateResponseDTO()', () => {
+ describe('Priority: fn toClientsCreateResponseDTO()', () => {
describe('Testing valid fn calls', () => {
test('Map timestamps of clients object, result: dto ClientsCreateResponseDTO', () => {
const mockParam_data: Clients = structuredClone(mockData);
- const testFn = clientsModel.mapToCreateResponseDTO(mockParam_data, mockVar_apiKey.keyRaw);
+ const testFn = clientsModel.toClientsCreateResponseDTO(mockParam_data, mockVar_apiKey.keyRaw);
const expectResult: ClientsCreateResponseDTO = {
client_id: mockParam_data.client_id,
name: mockParam_data.name,
@@ -52,10 +53,10 @@ describe('Unit-tests (model), priority: entity Clients', () => {
describe('Testing valid fn calls', () => {
test('Generate new object Clients + raw key', () => {
+ const mockParam_id = mockValidClientId;
const mockParam_dto: ClientsCreateDTO = {
name: 'TESTCLIENT'
};
- const mockParam_id = mockId.clients.valid[0];
const mockApiKeyObj = { keyRaw: 'test-key', keyHash: 'hashed-test-key' };
const mockClient: Clients = {
client_id: mockParam_id,
@@ -69,7 +70,7 @@ describe('Unit-tests (model), priority: entity Clients', () => {
};
jest.spyOn(CommonUtils, "generateUUID").mockReturnValue(mockParam_id);
- jest.spyOn(clientsModel, "_generateApiKeyObj").mockReturnValue(mockApiKeyObj);
+ jest.spyOn(clientsModel as any, "generateApiKeyObj").mockReturnValue(mockApiKeyObj);
jest.spyOn(CommonUtils, "getTimestampUTC").mockReturnValue(mockTimestamp);
const testFn = clientsModel.generateClientsCreateObj(mockParam_dto);
diff --git a/backend/tests/unit-tests/models/feedback-rating.model.test.ts b/backend/tests/unit-tests/models/feedback-rating.model.test.ts
index 7d205a6..6b7e997 100644
--- a/backend/tests/unit-tests/models/feedback-rating.model.test.ts
+++ b/backend/tests/unit-tests/models/feedback-rating.model.test.ts
@@ -1,9 +1,11 @@
import { FeedbackRatingCreateDTO, FeedbackRatingExtendedResponseDTO, FeedbackRatingResponseDTO, FeedbackRatingUpdateDTO } from "../../../src/dtos/feedback-rating.dto";
import feedbackRatingModel from "../../../src/models/feedback-rating.model";
+import { ClientsId } from "../../../src/repositories/interfaces/clients.entity.interface";
import { FeedbackRating } from "../../../src/repositories/interfaces/feedback-rating.entity.interface";
import * as CommonUtils from "../../../src/utils/common.utils";
import * as mockId from "../../mock-data/id.mock-data.json";
+const mockValidClientId = mockId.clients.valid[0] as ClientsId;
const mockTimestamp = '2025-01-01T14:00:09.000Z';
describe('Unit-tests (model), priority: entity FeedbackRating', () => {
@@ -14,7 +16,7 @@ describe('Unit-tests (model), priority: entity FeedbackRating', () => {
test('Generate new object, params: are undefined', () => {
const mockParam_dto: FeedbackRatingCreateDTO = {
- client_id: mockId.clients.valid[0]
+ client_id: mockValidClientId
};
jest.spyOn(CommonUtils, "getTimestampUTC").mockReturnValue(mockTimestamp);
@@ -32,7 +34,7 @@ describe('Unit-tests (model), priority: entity FeedbackRating', () => {
test('Generate new object, params: have values', () => {
const mockParam_dto: FeedbackRatingCreateDTO = {
- client_id: mockId.clients.valid[0],
+ client_id: mockValidClientId,
count: 1,
rating_sum: 5
};
@@ -78,7 +80,7 @@ describe('Unit-tests (model), priority: entity FeedbackRating', () => {
test('Convert entity to dto + map average rating, result: FeedbackRatingExtendedResponseDTO', () => {
const mockParam_entity: FeedbackRating = {
- client_id: mockId.clients.valid[0],
+ client_id: mockValidClientId,
count: 16,
rating_sum: 67,
last_modified: mockTimestamp,
@@ -97,7 +99,7 @@ describe('Unit-tests (model), priority: entity FeedbackRating', () => {
test('Convert entity to dto + map average rating, result: FeedbackRatingResponseDTO', () => {
const mockParam_entity: FeedbackRating = {
- client_id: mockId.clients.valid[0],
+ client_id: mockValidClientId,
count: 16,
rating_sum: 67,
last_modified: mockTimestamp,
@@ -121,7 +123,7 @@ describe('Unit-tests (model), priority: entity FeedbackRating', () => {
test('Convert entity[] to dto[] + map average rating, result: FeedbackRatingExtendedResponseDTO[]', () => {
const mockParam_entities: FeedbackRating[] = [{
- client_id: mockId.clients.valid[0],
+ client_id: mockValidClientId,
count: 16,
rating_sum: 67,
last_modified: mockTimestamp,
@@ -141,7 +143,7 @@ describe('Unit-tests (model), priority: entity FeedbackRating', () => {
test('Convert entity[] to dto[] + map average rating, result: FeedbackRatingResponseDTO[]', () => {
const mockParam_entities: FeedbackRating[] = [{
- client_id: mockId.clients.valid[0],
+ client_id: mockValidClientId,
count: 16,
rating_sum: 67,
last_modified: mockTimestamp,
diff --git a/backend/tests/unit-tests/models/feedback.model.test.ts b/backend/tests/unit-tests/models/feedback.model.test.ts
index f5b3d5e..34d1093 100644
--- a/backend/tests/unit-tests/models/feedback.model.test.ts
+++ b/backend/tests/unit-tests/models/feedback.model.test.ts
@@ -1,10 +1,15 @@
import { FeedbackRatingResponseDTO } from "../../../src/dtos/feedback-rating.dto";
import { FeedbackCreateDTO, FeedbackResponseDTO, FeedbackUpdateReviewDTO } from "../../../src/dtos/feedback.dto";
import feedbackModel from "../../../src/models/feedback.model";
-import { Feedback } from "../../../src/repositories/interfaces/feedback.entity.interface";
+import { ClientsId } from "../../../src/repositories/interfaces/clients.entity.interface";
+import { Feedback, FeedbackId } from "../../../src/repositories/interfaces/feedback.entity.interface";
+import { UsersId } from "../../../src/repositories/interfaces/users.entity.interface";
import * as CommonUtils from "../../../src/utils/common.utils";
import * as mockId from "../../mock-data/id.mock-data.json";
+const mockValidClientId = mockId.clients.valid[0] as ClientsId;
+const mockValidUserId = mockId.users.valid[0] as UsersId;
+const mockValidFeedbackId = mockId.feedback.valid[0] as FeedbackId;
const mockTimestamp = '2025-01-01T14:00:08.000Z';
describe('Unit-tests (model), priority: entity Feedback', () => {
@@ -12,9 +17,9 @@ describe('Unit-tests (model), priority: entity Feedback', () => {
let mockEntity: Feedback;
beforeEach(() => {
mockEntity = {
- feedback_id: mockId.feedback.valid[0],
- client_id: mockId.clients.valid[0],
- user_id: mockId.users.valid[0],
+ feedback_id: mockValidFeedbackId,
+ client_id: mockValidClientId,
+ user_id: mockValidUserId,
rating: 5,
term_accepted: true,
message: 'valid_feedback_test_message',
@@ -121,9 +126,9 @@ describe('Unit-tests (model), priority: entity Feedback', () => {
const originalEntity: Feedback = structuredClone(mockEntity);
originalEntity.reviewed_on = mockTimestamp;
const newEntity: Feedback = {
- feedback_id: mockId.feedback.valid[1],
- client_id: mockId.clients.valid[0],
- user_id: mockId.users.valid[1],
+ feedback_id: mockId.feedback.valid[1] as FeedbackId,
+ client_id: mockValidClientId,
+ user_id: mockId.users.valid[1] as UsersId,
rating: 4,
term_accepted: false,
message: 'another_valid_feedback_test_message',
diff --git a/backend/tests/unit-tests/models/rate-limits.models.test.ts b/backend/tests/unit-tests/models/rate-limits.models.test.ts
index 691aaee..d44d8ca 100644
--- a/backend/tests/unit-tests/models/rate-limits.models.test.ts
+++ b/backend/tests/unit-tests/models/rate-limits.models.test.ts
@@ -1,13 +1,19 @@
import { RateLimitsCreateDTO } from "../../../src/dtos/rate-limits.dto";
import demoLimitsModel from "../../../src/models/demo-limits.model";
import rateLimitsModel from "../../../src/models/rate-limits.model";
-import { DemoLimits } from "../../../src/repositories/interfaces/demo-limits.entity.interface";
-import { RateLimits } from "../../../src/repositories/interfaces/rate-limits.entity.interface";
+import { ClientsId } from "../../../src/repositories/interfaces/clients.entity.interface";
+import { DemoLimitsId, DemoLimits } from "../../../src/repositories/interfaces/demo-limits.entity.interface";
+import { RateLimitsId, RateLimits } from "../../../src/repositories/interfaces/rate-limits.entity.interface";
+import { UsersId } from "../../../src/repositories/interfaces/users.entity.interface";
import * as CommonUtils from "../../../src/utils/common.utils";
import { default as mockId } from "../../mock-data/id.mock-data.json";
describe('Unit-tests (model), priority: entity RateLimits', () => {
+ const mockValidRateLimitId = mockId.rate_limits.valid[0] as RateLimitsId;
+ const mockValidClientId = mockId.clients.valid[0] as ClientsId;
+ const mockValidUserId = mockId.users.valid[0] as UsersId;
+
describe('Priority: fn mapCounts()', () => {
describe('Testing valid fn calls', () => {
@@ -15,17 +21,17 @@ describe('Unit-tests (model), priority: entity RateLimits', () => {
test('Sum up count of entries, params: [].length === 2', () => {
const mockParam_data: RateLimits[] = [
{
- rate_limit_id: mockId.rate_limits.valid[0],
- client_id: mockId.clients.valid[0],
- user_id: mockId.users.valid[0],
+ rate_limit_id: mockValidRateLimitId,
+ client_id: mockValidClientId,
+ user_id: mockValidUserId,
day: '2025-01-01',
count: 3,
last_modified: '2025-01-01T14:00:05.000Z'
},
{
- rate_limit_id: mockId.rate_limits.valid[1],
- client_id: mockId.clients.valid[0],
- user_id: 'another_valid_users_test_id',
+ rate_limit_id: mockId.rate_limits.valid[1] as RateLimitsId,
+ client_id: mockValidClientId,
+ user_id: mockId.users.valid[1] as UsersId,
day: '2025-01-01',
count: 2,
last_modified: '2025-01-01T14:00:05.000Z'
@@ -41,9 +47,9 @@ describe('Unit-tests (model), priority: entity RateLimits', () => {
test('Sum up count of entries, params: [].length === 1', () => {
const mockParam_data: RateLimits[] = [
{
- rate_limit_id: mockId.rate_limits.valid[0],
- client_id: mockId.clients.valid[0],
- user_id: mockId.users.valid[0],
+ rate_limit_id: mockValidRateLimitId,
+ client_id: mockValidClientId,
+ user_id: mockValidUserId,
day: '2025-01-01',
count: 4,
last_modified: '2025-01-01T14:00:05.000Z'
@@ -73,8 +79,8 @@ describe('Unit-tests (model), priority: entity RateLimits', () => {
test('Map timestamp/date values to DTO, result: entity Partial', () => {
const mockParam_dto: RateLimitsCreateDTO = {
- client_id: mockId.clients.valid[0],
- user_id: mockId.users.valid[0],
+ client_id: mockValidClientId,
+ user_id: mockValidUserId,
}
const mockDate = '2025-01-01';
const mockTimestamp = '2025-01-01T14:00:05.000Z';
@@ -106,7 +112,7 @@ describe('Unit-tests (model), priority: entity DemoLimits', () => {
test('Sum up count of entries, params: [].length === 1', () => {
const mockParam_data: DemoLimits[] = [
{
- demo_limit_id: 1,
+ demo_limit_id: 1 as DemoLimitsId,
day: '2025-01-01',
count: 4,
last_modified: '2025-01-01T14:00:05.000Z'
diff --git a/backend/tests/unit-tests/models/tickets.model.test.ts b/backend/tests/unit-tests/models/tickets.model.test.ts
index 3ab61f4..1e5cee7 100644
--- a/backend/tests/unit-tests/models/tickets.model.test.ts
+++ b/backend/tests/unit-tests/models/tickets.model.test.ts
@@ -2,18 +2,23 @@ import { TicketsCreateDTO, TicketsResponseDTO, TicketsUpdateDTO } from "../../..
import * as CommonUtils from "../../../src/utils/common.utils";
import * as mockId from "../../mock-data/id.mock-data.json";
import { TicketStatus } from "../../../src/utils/enums/ticket-status.enum";
-import { Tickets } from "../../../src/repositories/interfaces/tickets.entity.interface";
+import { Tickets, TicketsId } from "../../../src/repositories/interfaces/tickets.entity.interface";
import ticketsModel from "../../../src/models/tickets.model";
import { Readable } from 'stream';
import { FilesService } from "../../../src/services/files.service";
import { TicketOption } from "../../../src/utils/enums/ticket-option.enum";
import { PermissionException } from "../../../src/utils/exceptions/auth.exception";
+import { ClientsId } from "../../../src/repositories/interfaces/clients.entity.interface";
+import { UsersId } from "../../../src/repositories/interfaces/users.entity.interface";
+const mockValidTicketId = mockId.tickets.valid[0] as TicketsId;
+const mockValidClientId = mockId.clients.valid[0] as ClientsId;
+const mockValidUserId = mockId.users.valid[0] as UsersId;
const mockTimestamp = '2025-01-01T14:00:04.000Z';
describe('Unit-tests (model), priority: entity Tickets', () => {
- describe('Priority: fn generateTicket()', () => {
+ describe('Priority: fn generateTicketEntity()', () => {
let mockFile_pdf: Express.Multer.File;
let mockFile_webp: Express.Multer.File;
@@ -50,10 +55,10 @@ describe('Unit-tests (model), priority: entity Tickets', () => {
describe('Testing valid fn calls', () => {
test('Generate new object, priority: no files', async () => {
- const mockParam_id = mockId.tickets.new[0];
+ const mockParam_id = mockId.tickets.new[0] as TicketsId;
const mockParam_dto: TicketsCreateDTO = {
- client_id: mockId.clients.valid[0],
- user_id: mockId.users.valid[0],
+ client_id: mockValidClientId,
+ user_id: mockValidUserId,
option: TicketOption.SUPPORT,
title: 'test-title',
message: 'test-message'
@@ -63,7 +68,7 @@ describe('Unit-tests (model), priority: entity Tickets', () => {
jest.spyOn(CommonUtils, "generateUUID").mockReturnValue(mockParam_id);
jest.spyOn(CommonUtils, "getTimestampUTC").mockReturnValue(mockTimestamp);
- const testFn = await ticketsModel.generateTicket(mockParam_dto, mockParam_files);
+ const testFn = await ticketsModel.generateTicketEntity(mockParam_dto, mockParam_files);
const expectResult: Tickets = {
ticket_id: mockParam_id,
client_id: mockParam_dto.client_id,
@@ -81,10 +86,10 @@ describe('Unit-tests (model), priority: entity Tickets', () => {
})
test('Generate new object, priority: single file', async () => {
- const mockParam_id = mockId.tickets.new[0];
+ const mockParam_id = mockId.tickets.new[0] as TicketsId;
const mockParam_dto: TicketsCreateDTO = {
- client_id: mockId.clients.valid[0],
- user_id: mockId.users.valid[0],
+ client_id: mockValidClientId,
+ user_id: mockValidUserId,
option: TicketOption.SUPPORT,
title: 'test-title',
message: 'test-message'
@@ -98,7 +103,7 @@ describe('Unit-tests (model), priority: entity Tickets', () => {
jest.spyOn(FilesService.prototype, 'uploadFiles').mockImplementation();
jest.spyOn(FilesService.prototype, 'getResourcePaths').mockReturnValue(mockPaths);
- const testFn = await ticketsModel.generateTicket(mockParam_dto, mockParam_files);
+ const testFn = await ticketsModel.generateTicketEntity(mockParam_dto, mockParam_files);
const expectResult: Tickets = {
ticket_id: mockParam_id,
client_id: mockParam_dto.client_id,
@@ -117,10 +122,10 @@ describe('Unit-tests (model), priority: entity Tickets', () => {
})
test('Generate new object, priority: multiple files', async () => {
- const mockParam_id = mockId.tickets.new[0];
+ const mockParam_id = mockId.tickets.new[0] as TicketsId;
const mockParam_dto: TicketsCreateDTO = {
- client_id: mockId.clients.valid[0],
- user_id: mockId.users.valid[0],
+ client_id: mockValidClientId,
+ user_id: mockValidUserId,
option: TicketOption.SUPPORT,
title: 'test-title',
message: 'test-message'
@@ -137,7 +142,7 @@ describe('Unit-tests (model), priority: entity Tickets', () => {
jest.spyOn(FilesService.prototype, 'uploadFiles').mockImplementation();
jest.spyOn(FilesService.prototype, 'getResourcePaths').mockReturnValue(mockPaths);
- const testFn = await ticketsModel.generateTicket(mockParam_dto, mockParam_files);
+ const testFn = await ticketsModel.generateTicketEntity(mockParam_dto, mockParam_files);
const expectResult: Tickets = {
ticket_id: mockParam_id,
client_id: mockParam_dto.client_id,
@@ -190,9 +195,9 @@ describe('Unit-tests (model), priority: entity Tickets', () => {
test('Check for file deletion => call FilesService.deleteFiles(), params: ', async () => {
const mockParam_dto: TicketsResponseDTO = {
- ticket_id: mockId.tickets.valid[0],
- client_id: mockId.clients.valid[0],
- user_id: mockId.users.valid[0],
+ ticket_id: mockValidTicketId,
+ client_id: mockValidClientId,
+ user_id: mockValidUserId,
status: TicketStatus.ISSUED,
option: TicketOption.SUPPORT,
title: 'test-title',
@@ -214,9 +219,9 @@ describe('Unit-tests (model), priority: entity Tickets', () => {
test('Check for file deletion => does NOT call FilesService.deleteFiles(), params: ', async () => {
const mockParam_dto: TicketsResponseDTO = {
- ticket_id: mockId.tickets.valid[1],
- client_id: mockId.clients.valid[0],
- user_id: mockId.users.valid[0],
+ ticket_id: mockId.tickets.valid[1] as TicketsId,
+ client_id: mockValidClientId,
+ user_id: mockValidUserId,
status: TicketStatus.ISSUED,
option: TicketOption.SUPPORT,
title: 'test-title-without-resource_paths',
@@ -239,9 +244,9 @@ describe('Unit-tests (model), priority: entity Tickets', () => {
let mockParam_dto: TicketsResponseDTO;
beforeEach(() => {
mockParam_dto = {
- ticket_id: mockId.tickets.valid[0],
- client_id: mockId.clients.valid[0],
- user_id: mockId.users.valid[0],
+ ticket_id: mockValidTicketId,
+ client_id: mockValidClientId,
+ user_id: mockValidUserId,
status: TicketStatus.CLOSED,
option: TicketOption.SUPPORT,
title: 'test-title',
diff --git a/backend/tests/unit-tests/models/users.models.test.ts b/backend/tests/unit-tests/models/users.models.test.ts
index c40b5da..4476869 100644
--- a/backend/tests/unit-tests/models/users.models.test.ts
+++ b/backend/tests/unit-tests/models/users.models.test.ts
@@ -1,20 +1,21 @@
-import { Users } from "../../../src/repositories/interfaces/users.entity.interface";
+import { Users, UsersId } from "../../../src/repositories/interfaces/users.entity.interface";
import { UsersCreateDTO } from "../../../src/dtos/users.dto";
import usersModel from "../../../src/models/users.model";
import * as CommonUtils from "../../../src/utils/common.utils";
import { default as mockId } from "../../mock-data/id.mock-data.json";
import { UserStatus } from "../../../src/utils/enums/user-status.enum";
+const mockValidUserId = mockId.users.valid[0] as UsersId;
const mockTimestamp = '2025-01-03T14:00:03.000Z';
describe('Unit-tests (model), priority: entity Users', () => {
- describe('Priority: fn generateUser()', () => {
+ describe('Priority: fn generateUserEntity()', () => {
describe('Testing valid fn calls', () => {
test('Generate new user object, params: valid ', () => {
- const mockParam_id = mockId.users.valid[0];
+ const mockParam_id = mockValidUserId;
const mockParam_dto: UsersCreateDTO = {
email: 'valid.user@test.com'
};
@@ -22,9 +23,9 @@ describe('Unit-tests (model), priority: entity Users', () => {
jest.spyOn(CommonUtils, "generateUUID").mockReturnValue(mockParam_id);
jest.spyOn(CommonUtils, "getTimestampUTC").mockReturnValue(mockTimestamp);
- const testFn = usersModel.generateUser(mockParam_dto);
+ const testFn = usersModel.generateUserEntity(mockParam_dto);
const expectResult: Users = {
- user_id: mockParam_id,
+ user_id: mockValidUserId,
email: mockParam_dto.email,
status: UserStatus.ACTIVE,
flag: null,
diff --git a/backend/tests/unit-tests/repositories/clients.repositories.test.ts b/backend/tests/unit-tests/repositories/clients.repositories.test.ts
index 41d4925..229e8ef 100644
--- a/backend/tests/unit-tests/repositories/clients.repositories.test.ts
+++ b/backend/tests/unit-tests/repositories/clients.repositories.test.ts
@@ -10,7 +10,7 @@ import { DBConnection } from "../../../src/configs/db";
import * as CommonUtils from "../../../src/utils/common.utils";
import * as MockUtils from "../../common.test-utils";
import { default as mockId } from "../../mock-data/id.mock-data.json";
-import { Clients } from "../../../src/repositories/interfaces/clients.entity.interface";
+import { Clients, ClientsId } from "../../../src/repositories/interfaces/clients.entity.interface";
import { ApiKeyStatus } from "../../../src/utils/enums/api-key-status.enum";
import clientsRepository from "../../../src/repositories/clients.repository";
import clientsModel from "../../../src/models/clients.model";
@@ -26,11 +26,11 @@ jest.mock("../../../src/configs/db", () => {
}
});
-const mockVar_id = '9e024539-32e8-4317-8007-84a3956e6b57';
+const mockValidClientId = mockId.clients.valid[0] as ClientsId;
const mockVar_keyHash = secrets.TEST_APIKEY_HASH;
const mockTimestamp = '2025-01-01T14:00:02.000Z';
const mockData: Clients = {
- client_id: mockVar_id,
+ client_id: mockValidClientId,
name: 'valid_clients_test_name',
api_key_hash: mockVar_keyHash,
status: ApiKeyStatus.ACTIVE,
@@ -56,18 +56,18 @@ describe('Unit-tests (repository), priority: entity Clients', () => {
test('Return data for existing entry, params: valid ', async () => {
const mockResult: Clients | null = structuredClone(mockData);
const mockClient = MockUtils.mapMockDbClient(mockResult);
- const testFn = await clientsRepository.findById(mockVar_id);
+ const testFn = await clientsRepository.findById(mockValidClientId);
expect(testFn).toEqual(mockResult);
expect(DBConnection.getInstance).toHaveBeenCalled();
expect(mockClient.query).toHaveBeenCalledWith(
expect.stringContaining(sql),
- expect.arrayContaining([mockVar_id])
+ expect.arrayContaining([mockValidClientId])
);
})
test('Return null for non-existing entry, params: invalid ', async () => {
- const mockParam_id = mockId.clients.invalid[0];
+ const mockParam_id = mockId.clients.invalid[0] as ClientsId;
const mockResult: Clients | null = null;
const mockClient = MockUtils.mapMockDbClient(mockResult);
const testFn = await clientsRepository.findById(mockParam_id);
@@ -84,7 +84,7 @@ describe('Unit-tests (repository), priority: entity Clients', () => {
describe('Testing invalid fn calls', () => {
test('Throw DBQueryErrorException by catch-block', async () => {
- const mockParam_id = mockId.clients.invalid[0];
+ const mockParam_id = mockId.clients.invalid[0] as ClientsId;
const mockErrorMsg = "DB ERROR ON SELECT QUERY";
const mockResult: Clients | null = null;
jest.spyOn(CommonUtils, "logError").mockReturnValue();
@@ -161,7 +161,7 @@ describe('Unit-tests (repository), priority: entity Clients', () => {
test('Return data for existing entry, params: ', async () => {
const mockParam_name = 'existing_clients_test_name';
const mockResult: ClientsStatusResponseDTO = {
- client_id: 'valid_clients_test_id',
+ client_id: mockValidClientId,
name: mockParam_name,
status: ApiKeyStatus.ACTIVE,
last_use: mockTimestamp,
@@ -217,9 +217,9 @@ describe('Unit-tests (repository), priority: entity Clients', () => {
let mockParam_entity: Clients;
beforeEach(() => {
sql = `INSERT`;
- mockVar_apiKey = clientsModel._generateApiKeyObj();
+ mockVar_apiKey = (clientsModel as any).generateApiKeyObj();
mockParam_entity = {
- client_id: mockVar_id,
+ client_id: mockValidClientId,
name: 'valid_clients_test_name',
api_key_hash: mockVar_apiKey.keyHash,
status: ApiKeyStatus.ACTIVE,
@@ -275,7 +275,7 @@ describe('Unit-tests (repository), priority: entity Clients', () => {
describe('Testing valid fn calls', () => {
test('Return data of changed entry, params: valid ', async () => {
- const mockParam_id = mockId.clients.valid[0];
+ const mockParam_id = mockValidClientId;
const mockValues = [mockParam_dto.status, mockTimestamp, mockParam_id];
const mockResult: ClientsStatusResponseDTO = {
@@ -298,7 +298,7 @@ describe('Unit-tests (repository), priority: entity Clients', () => {
})
test('Return null for non-existing entry by invalid id', async () => {
- const mockParam_id = mockId.clients.invalid[0];
+ const mockParam_id = mockId.clients.invalid[0] as ClientsId;
const mockValues = [mockParam_dto.status, mockTimestamp, mockParam_id];
const mockResult = null;
@@ -317,7 +317,7 @@ describe('Unit-tests (repository), priority: entity Clients', () => {
describe('Testing invalid fn calls', () => {
test('Throw DBQueryErrorException by catch-block', async () => {
- const mockParam_id = mockId.clients.invalid[0];
+ const mockParam_id = mockId.clients.invalid[0] as ClientsId;
const mockErrorMsg = "DB ERROR ON UPDATE QUERY";
const mockResult = null;
jest.spyOn(CommonUtils, "logError").mockReturnValue();
@@ -341,7 +341,7 @@ describe('Unit-tests (repository), priority: entity Clients', () => {
describe('Testing valid fn calls', () => {
test('Return data of changed entry, params: valid ', async () => {
- const mockParam_id = mockId.clients.valid[0];
+ const mockParam_id = mockValidClientId;
const mockValues = [mockParam_dto.flag, mockTimestamp, mockParam_id];
const mockResult: ClientsFlagResponseDTO | null = {
@@ -363,7 +363,7 @@ describe('Unit-tests (repository), priority: entity Clients', () => {
})
test('Return null for non-existing entry, params: invalid ', async () => {
- const mockParam_id = mockId.clients.invalid[0];
+ const mockParam_id = mockId.clients.invalid[0] as ClientsId;
const mockValues = [mockParam_dto.flag, mockTimestamp, mockParam_id];
const mockResult: ClientsFlagResponseDTO | null = null;
@@ -382,7 +382,7 @@ describe('Unit-tests (repository), priority: entity Clients', () => {
describe('Testing invalid fn calls', () => {
test('Throw DBQueryErrorException by catch-block', async () => {
- const mockParam_id = mockId.clients.invalid[0];
+ const mockParam_id = mockId.clients.invalid[0] as ClientsId;
const mockErrorMsg = "DB ERROR ON UPDATE QUERY";
const mockResult: ClientsFlagResponseDTO | null = null;
jest.spyOn(CommonUtils, "logError").mockReturnValue();
@@ -408,7 +408,7 @@ describe('Unit-tests (repository), priority: entity Clients', () => {
describe('Testing valid fn calls', () => {
test('Return data of changed entry, params: valid ', async () => {
- const mockParam_id = mockId.clients.valid[0];
+ const mockParam_id = mockValidClientId;
const mockValues = [mockTimestamp, mockParam_id];
jest.spyOn(CommonUtils, "getTimestampUTC").mockReturnValue(mockTimestamp);
@@ -432,7 +432,7 @@ describe('Unit-tests (repository), priority: entity Clients', () => {
})
test('Return null for non-existing entry, params: invalid ', async () => {
- const mockParam_id = mockId.clients.invalid[0];
+ const mockParam_id = mockId.clients.invalid[0] as ClientsId;
const mockValues = [mockTimestamp, mockParam_id];
const mockResult = null;
@@ -451,7 +451,7 @@ describe('Unit-tests (repository), priority: entity Clients', () => {
describe('Testing invalid fn calls', () => {
test('Throw DBQueryErrorException by catch-block', async () => {
- const mockParam_id = mockId.clients.invalid[0];
+ const mockParam_id = mockId.clients.invalid[0] as ClientsId;
const mockErrorMsg = "DB ERROR ON UPDATE QUERY";
const mockResult = null;
jest.spyOn(CommonUtils, "logError").mockReturnValue();
diff --git a/backend/tests/unit-tests/repositories/db.repositories.test.ts b/backend/tests/unit-tests/repositories/db.repositories.test.ts
index cb582da..a9d667f 100644
--- a/backend/tests/unit-tests/repositories/db.repositories.test.ts
+++ b/backend/tests/unit-tests/repositories/db.repositories.test.ts
@@ -42,17 +42,17 @@ describe('Unit-tests (repository), class DBConnection', () => {
describe('Testing valid fn calls', () => {
- test('Fn _getConnectionString(), environment: development', () => {
+ test('Fn getConnectionString(), environment: development', () => {
const mockParam_env = 'development';
- const testFn = mockDb._getConnectionString(mockParam_env);
+ const testFn = mockDb.getConnectionString(mockParam_env);
const expectUser = 'postgres';
expect(testFn).toContain(expectUser);
})
- test('Fn _getConnectionString(), environment: test', () => {
+ test('Fn getConnectionString(), environment: test', () => {
const mockParam_env = 'test';
- const testFn = mockDb._getConnectionString(mockParam_env);
+ const testFn = mockDb.getConnectionString(mockParam_env);
const expectUser = 'testuser';
expect(testFn).toContain(expectUser);
diff --git a/backend/tests/unit-tests/repositories/feedback-rating.repositories.test.ts b/backend/tests/unit-tests/repositories/feedback-rating.repositories.test.ts
index 8e1fea1..182f062 100644
--- a/backend/tests/unit-tests/repositories/feedback-rating.repositories.test.ts
+++ b/backend/tests/unit-tests/repositories/feedback-rating.repositories.test.ts
@@ -6,6 +6,7 @@ import { default as mockId } from "../../mock-data/id.mock-data.json";
import feedbackRatingRepository from "../../../src/repositories/feedback-rating.repository";
import { DBConnection } from "../../../src/configs/db";
import { FeedbackRatingUpdateDTO } from "../../../src/dtos/feedback-rating.dto";
+import { ClientsId } from "../../../src/repositories/interfaces/clients.entity.interface";
jest.mock("../../../src/configs/db", () => {
return {
@@ -15,9 +16,10 @@ jest.mock("../../../src/configs/db", () => {
}
});
+const mockValidClientId = mockId.clients.valid[0] as ClientsId;
const mockTimestamp = '2025-01-01T14:00:09.000Z';
const mockData: FeedbackRating = {
- client_id: mockId.clients.valid[0],
+ client_id: mockValidClientId,
count: 16,
rating_sum: 67,
last_modified: mockTimestamp,
@@ -38,7 +40,7 @@ describe('Unit-tests (repository), priority: entity FeedbackRating', () => {
});
test('Return data for existing entry, params: valid ', async () => {
- const mockParam_id = mockId.clients.valid[0];
+ const mockParam_id = mockValidClientId;
const mockResult: FeedbackRating = structuredClone(mockData);
const mockClient = MockUtils.mapMockDbClient(mockResult);
const testFn = await feedbackRatingRepository.findById(mockParam_id);
@@ -52,7 +54,7 @@ describe('Unit-tests (repository), priority: entity FeedbackRating', () => {
})
test('Return null for non-existing entry, params: non-existing ', async () => {
- const mockParam_id = mockId.clients.invalid[0];
+ const mockParam_id = mockId.clients.invalid[0] as ClientsId;
const mockResult = null;
const mockClient = MockUtils.mapMockDbClient(mockResult);
const testFn = await feedbackRatingRepository.findById(mockParam_id);
@@ -69,7 +71,7 @@ describe('Unit-tests (repository), priority: entity FeedbackRating', () => {
describe('Testing invalid fn calls', () => {
test('Throw DBQueryErrorException by catch-block', async () => {
- const mockParam_id = mockId.clients.invalid[0];
+ const mockParam_id = mockId.clients.invalid[0] as ClientsId;
const mockErrorMsg = "DB ERROR ON SELECT QUERY";
const mockResult = null;
jest.spyOn(CommonUtils, "logError").mockReturnValue();
@@ -146,7 +148,7 @@ describe('Unit-tests (repository), priority: entity FeedbackRating', () => {
test('Return data for multiple existing entries', async () => {
const mockData_entry0: FeedbackRating = structuredClone(mockData);
const mockData_entry1: FeedbackRating = {
- client_id: 'another_clients_test_id',
+ client_id: mockId.clients.valid[1] as ClientsId,
count: 5,
rating_sum: 19,
last_modified: mockTimestamp,
@@ -201,7 +203,7 @@ describe('Unit-tests (repository), priority: entity FeedbackRating', () => {
beforeEach(() => {
sql = `INSERT`;
mockParam_entity = {
- client_id: mockId.clients.new[0],
+ client_id: mockValidClientId,
count: 0,
rating_sum: 0,
last_modified: mockTimestamp,
@@ -265,7 +267,7 @@ describe('Unit-tests (repository), priority: entity FeedbackRating', () => {
})
test('Return null for non-existing entry, params: invalid ', async () => {
- const mockParam_id = mockId.clients.invalid[0];
+ const mockParam_id = mockId.clients.invalid[0] as ClientsId;
const mockValues: any[] = [mockParam_dto.rating];
const mockResult: FeedbackRating | null = null;
diff --git a/backend/tests/unit-tests/repositories/feedback.repositories.test.ts b/backend/tests/unit-tests/repositories/feedback.repositories.test.ts
index db760fd..712d5c2 100644
--- a/backend/tests/unit-tests/repositories/feedback.repositories.test.ts
+++ b/backend/tests/unit-tests/repositories/feedback.repositories.test.ts
@@ -2,10 +2,12 @@ import * as MockUtils from "../../common.test-utils";
import * as CommonUtils from "../../../src/utils/common.utils";
import { default as mockId } from "../../mock-data/id.mock-data.json";
import { DBQueryErrorException } from "../../../src/utils/exceptions/db.exception";
-import { Feedback } from "../../../src/repositories/interfaces/feedback.entity.interface";
+import { Feedback, FeedbackId } from "../../../src/repositories/interfaces/feedback.entity.interface";
import { DBConnection } from "../../../src/configs/db";
import feedbackRepository from "../../../src/repositories/feedback.repository";
import { FeedbackFilterDTO, FeedbackUpdateReviewDTO } from "../../../src/dtos/feedback.dto";
+import { UsersId } from "../../../src/repositories/interfaces/users.entity.interface";
+import { ClientsId } from "../../../src/repositories/interfaces/clients.entity.interface";
jest.mock("../../../src/configs/db", () => {
return {
@@ -15,11 +17,12 @@ jest.mock("../../../src/configs/db", () => {
}
});
+const mockValidFeedbackId = mockId.feedback.valid[0] as FeedbackId;
const mockTimestamp = '2025-01-01T14:00:08.000Z';
const mockData: Feedback = {
- feedback_id: mockId.feedback.valid[0],
- client_id: mockId.clients.valid[0],
- user_id: mockId.users.valid[0],
+ feedback_id: mockValidFeedbackId,
+ client_id: mockId.clients.valid[0] as ClientsId,
+ user_id: mockId.users.valid[0] as UsersId,
rating: 5,
term_accepted: true,
message: 'valid_feedback_test_message',
@@ -41,7 +44,7 @@ describe('Unit-tests (repository), priority: entity Feedback', () => {
});
test('Return data for existing entry, params: valid ', async () => {
- const mockParam_id = mockId.feedback.valid[0];
+ const mockParam_id = mockValidFeedbackId;
const mockResult: Feedback = structuredClone(mockData);
const mockClient = MockUtils.mapMockDbClient(mockResult);
const testFn = await feedbackRepository.findById(mockParam_id);
@@ -55,7 +58,7 @@ describe('Unit-tests (repository), priority: entity Feedback', () => {
})
test('Return null for non-existing entry, params: non-existing ', async () => {
- const mockParam_id = mockId.feedback.invalid[0];
+ const mockParam_id = mockId.feedback.invalid[0] as FeedbackId;
const mockResult = null;
const mockClient = MockUtils.mapMockDbClient(mockResult);
const testFn = await feedbackRepository.findById(mockParam_id);
@@ -72,7 +75,7 @@ describe('Unit-tests (repository), priority: entity Feedback', () => {
describe('Testing invalid fn calls', () => {
test('Throw DBQueryErrorException by catch-block', async () => {
- const mockParam_id = mockId.feedback.invalid[0];
+ const mockParam_id = mockId.feedback.invalid[0] as FeedbackId;
const mockErrorMsg = "DB ERROR ON SELECT QUERY";
const mockResult = null;
jest.spyOn(CommonUtils, "logError").mockReturnValue();
@@ -112,7 +115,9 @@ describe('Unit-tests (repository), priority: entity Feedback', () => {
})
test('Return null for non-existing entry, params: non-existing ', async () => {
- const mockParam_dto = { user_id: [mockId.users.invalid[0], 'another_invalid_users_test_id'] };
+ const mockParam_dto = {
+ user_id: [mockId.users.invalid[0], 'another_invalid_users_test_id'] as UsersId[]
+ };
const mockValues = mockParam_dto.user_id;
const mockResult: Feedback[] | null = null;
@@ -184,7 +189,7 @@ describe('Unit-tests (repository), priority: entity Feedback', () => {
describe('Testing valid fn calls', () => {
test('Return data of changed entry, params: valid ', async () => {
- const mockParam_id = mockId.feedback.valid[0];
+ const mockParam_id = mockValidFeedbackId;
const mockValues: any[] = [mockParam_dto.reviewed_on, mockParam_dto.last_modified];
const mockResult: Feedback | null = structuredClone(mockData);
@@ -200,7 +205,7 @@ describe('Unit-tests (repository), priority: entity Feedback', () => {
})
test('Return data of changed entry, params: invalid ', async () => {
- const mockParam_id = mockId.feedback.invalid[0];
+ const mockParam_id = mockId.feedback.invalid[0] as FeedbackId;
const mockValues: any[] = [mockParam_dto.reviewed_on, mockParam_dto.last_modified];
const mockResult: Feedback | null = null;
@@ -219,7 +224,7 @@ describe('Unit-tests (repository), priority: entity Feedback', () => {
describe('Testing invalid fn calls', () => {
test('', async () => {
- const mockParam_id = mockId.feedback.invalid[0];
+ const mockParam_id = mockId.feedback.invalid[0] as FeedbackId;
const mockErrorMsg = "DB ERROR ON UPDATE QUERY";
const mockResult = null;
jest.spyOn(CommonUtils, "logError").mockReturnValue();
diff --git a/backend/tests/unit-tests/repositories/meta.repositories.test.ts b/backend/tests/unit-tests/repositories/meta.repositories.test.ts
index 372045b..574400d 100644
--- a/backend/tests/unit-tests/repositories/meta.repositories.test.ts
+++ b/backend/tests/unit-tests/repositories/meta.repositories.test.ts
@@ -1,6 +1,6 @@
import { DBConnection } from "../../../src/configs/db";
import metaRepository from "../../../src/repositories/meta.repository";
-import { Maintenance, Meta } from "../../../src/repositories/interfaces/meta.entity.interface";
+import { Maintenance, Meta, MetaId } from "../../../src/repositories/interfaces/meta.entity.interface";
import * as CommonUtils from "../../../src/utils/common.utils";
import * as MockUtils from "../../common.test-utils";
import { MaintenanceMode } from "../../../src/utils/enums/maintenance-mode.enum";
@@ -17,9 +17,10 @@ jest.mock("../../../src/configs/db", () => {
}
});
+const mockValidMetaId = mockId.meta.valid[0] as MetaId;
const mockTimestamp = '2025-01-01T14:00:01.000Z';
const mockData: Meta = {
- id: 1,
+ id: mockValidMetaId,
app: "support",
author: "yqni13",
build_on: mockTimestamp,
@@ -48,7 +49,7 @@ describe('Unit-tests (repository), priority: entity Meta', () => {
});
test('Return data for existing entry, params: = 1', async () => {
- const mockParam_id = mockId.meta.valid[0];
+ const mockParam_id = mockValidMetaId;
const mockResult: Meta = structuredClone(mockData);
const mockClient = MockUtils.mapMockDbClient(mockResult);
const testFn = await metaRepository.findById(mockParam_id);
@@ -62,7 +63,7 @@ describe('Unit-tests (repository), priority: entity Meta', () => {
})
test('Return null for non-existing entry, params: = 0', async () => {
- const mockParam_id = mockId.meta.invalid[0];
+ const mockParam_id = mockId.meta.invalid[0] as MetaId;
const mockResult = null;
const mockClient = MockUtils.mapMockDbClient(mockResult);
const testFn = await metaRepository.findById(mockParam_id);
@@ -79,7 +80,7 @@ describe('Unit-tests (repository), priority: entity Meta', () => {
describe('Testing invalid fn calls', () => {
test('Throw DBQueryErrorException by catch-block', async () => {
- const mockParam_id = mockId.meta.invalid[0];
+ const mockParam_id = mockId.meta.invalid[0] as MetaId;
const mockErrorMsg = "DB ERROR ON SELECT QUERY";
const mockResult = null;
jest.spyOn(CommonUtils, "logError").mockReturnValue();
@@ -156,7 +157,7 @@ describe('Unit-tests (repository), priority: entity Meta', () => {
test('Return data for multiple existing entries', async () => {
const mockData_entry0 = structuredClone(mockData);
const mockData_entry1 = structuredClone(mockData_entry0);
- mockData_entry1['id'] = 2;
+ mockData_entry1['id'] = 2 as MetaId;
mockData_entry1['app'] = 'valid_meta_test_name';
const mockResult: Meta[] = [mockData_entry0, mockData_entry1];
@@ -206,7 +207,7 @@ describe('Unit-tests (repository), priority: entity Meta', () => {
});
test('Return data for existing entry, params: = "valid_meta_test_name"', async () => {
- const mockParam_id = mockId.meta.valid[0];;
+ const mockParam_id = mockValidMetaId;
const mockParam_name = 'valid_meta_test_name';
const mockResult: Maintenance = {
id: mockParam_id,
@@ -266,7 +267,7 @@ describe('Unit-tests (repository), priority: entity Meta', () => {
describe('Testing valid fn calls', () => {
test('Return data of changed entry, params: valid ', async () => {
- const mockParam_id = mockId.meta.valid[0];
+ const mockParam_id = mockValidMetaId;
const mockValues: any[] = Object.values(mockParam_dto).map(value => value);
mockValues.push(mockParam_id);
const mockResult: Meta = structuredClone(mockData);
@@ -285,7 +286,7 @@ describe('Unit-tests (repository), priority: entity Meta', () => {
})
test('Return null for non-existing entry, params: invalid ', async () => {
- const mockParam_id = mockId.meta.invalid[0];
+ const mockParam_id = mockId.meta.invalid[0] as MetaId;
const mockValues: any[] = Object.values(mockParam_dto).map(value => value);
mockValues.push(mockParam_id);
@@ -307,7 +308,7 @@ describe('Unit-tests (repository), priority: entity Meta', () => {
describe('Testing invalid fn calls', () => {
test('Throw DBQueryErrorException by catch-block', async () => {
- const mockParam_id = mockId.meta.invalid[0];
+ const mockParam_id = mockId.meta.invalid[0] as MetaId;
const mockErrorMsg = "DB ERROR ON UPDATE QUERY";
const mockResult = null;
jest.spyOn(CommonUtils, "logError").mockReturnValue();
@@ -336,7 +337,7 @@ describe('Unit-tests (repository), priority: entity Meta', () => {
describe('Testing valid fn calls', () => {
test('Return data of changed entry, params: valid ', async () => {
- const mockParam_id = mockId.meta.valid[0];
+ const mockParam_id = mockValidMetaId;
mockValues = [mockParam_dto.maintenance_mode, mockParam_dto.last_modified, mockParam_id];
const mockResult: Maintenance = {
id: mockData.id,
@@ -362,7 +363,7 @@ describe('Unit-tests (repository), priority: entity Meta', () => {
})
test('Return null for non-existing entry, params: invalid ', async () => {
- const mockParam_id = mockId.meta.invalid[0];
+ const mockParam_id = mockId.meta.invalid[0] as MetaId;
mockValues = [mockParam_dto.maintenance_mode, mockParam_dto.last_modified, mockParam_id];
const mockResult = null;
@@ -382,7 +383,7 @@ describe('Unit-tests (repository), priority: entity Meta', () => {
describe('Testing invalid fn calls', () => {
test('Throw DBQueryErrorException by catch-block', async () => {
- const mockParam_id = mockId.meta.invalid[0];
+ const mockParam_id = mockId.meta.invalid[0] as MetaId;
const mockErrorMsg = "DB ERROR ON UPDATE QUERY";
const mockResult = null;
jest.spyOn(CommonUtils, "logError").mockReturnValue();
diff --git a/backend/tests/unit-tests/repositories/rate-limits.repositories.test.ts b/backend/tests/unit-tests/repositories/rate-limits.repositories.test.ts
index c4915dd..c738bb5 100644
--- a/backend/tests/unit-tests/repositories/rate-limits.repositories.test.ts
+++ b/backend/tests/unit-tests/repositories/rate-limits.repositories.test.ts
@@ -9,10 +9,12 @@ import { DBQueryErrorException } from "../../../src/utils/exceptions/db.exceptio
import * as MockUtils from "../../common.test-utils";
import * as CommonUtils from "../../../src/utils/common.utils";
import { default as mockId } from "../../mock-data/id.mock-data.json";
-import { RateLimits } from "../../../src/repositories/interfaces/rate-limits.entity.interface";
-import { DemoLimits } from "../../../src/repositories/interfaces/demo-limits.entity.interface";
+import { RateLimitsId, RateLimits } from "../../../src/repositories/interfaces/rate-limits.entity.interface";
+import { DemoLimitsId, DemoLimits } from "../../../src/repositories/interfaces/demo-limits.entity.interface";
import { DemoLimitsCountDTO, DemoLimitsResponseDTO, DemoLimitsUpdateDTO } from "../../../src/dtos/demo-limits.dto";
import demoLimitsRepository from "../../../src/repositories/demo-limits.repository";
+import { ClientsId } from "../../../src/repositories/interfaces/clients.entity.interface";
+import { UsersId } from "../../../src/repositories/interfaces/users.entity.interface";
jest.mock("../../../src/configs/db", () => {
return {
@@ -22,10 +24,11 @@ jest.mock("../../../src/configs/db", () => {
}
});
+const mockValidRateLimitId = mockId.rate_limits.valid[0] as RateLimitsId;
+const mockValidClientId = mockId.clients.valid[0] as ClientsId;
+const mockValidUserId = mockId.users.valid[0] as UsersId;
const mockTimestamp = '2025-01-01T14:00:05.000Z';
const mockDate = '2025-01-01';
-const mockValidClientsId = mockId.clients.valid[0];
-const mockValidUsersId = mockId.users.valid[0];
const expectExceptionResult = DBQueryErrorException;
const mockBoolean = false;
@@ -38,7 +41,7 @@ describe('Unit-tests (repository), priority: entity RateLimits', () => {
let mockParam_dto_countById: RateLimitsCountDTO;
let mockParam_dto_countByDate: RateLimitsCountDTO;
beforeEach(() => {
- mockParam_dto_countById = { client_id: mockValidClientsId, day: '2025-01-01' };
+ mockParam_dto_countById = { client_id: mockValidClientId, day: '2025-01-01' };
mockParam_dto_countByDate = { day: '2025-02-05' };
})
@@ -47,9 +50,9 @@ describe('Unit-tests (repository), priority: entity RateLimits', () => {
test('Return entries by id and day, params: ', async () => {
const mockResult: RateLimits[] = [
{
- rate_limit_id: mockId.rate_limits.valid[0],
- client_id: mockValidClientsId,
- user_id: mockValidUsersId,
+ rate_limit_id: mockValidRateLimitId,
+ client_id: mockValidClientId,
+ user_id: mockValidUserId,
day: '2025-01-01',
count: 3,
last_modified: '2025-01-01T14:00:05.000Z'
@@ -73,17 +76,17 @@ describe('Unit-tests (repository), priority: entity RateLimits', () => {
test('Return entries by day, params: ', async () => {
const mockResult: RateLimits[] = [
{
- rate_limit_id: mockId.rate_limits.new[0],
- client_id: mockValidClientsId,
- user_id: mockValidUsersId,
+ rate_limit_id: mockId.rate_limits.new[0] as RateLimitsId,
+ client_id: mockValidClientId,
+ user_id: mockValidUserId,
day: '2025-02-05',
count: 1,
last_modified: '2025-02-05T04:17:05.000Z'
},
{
- rate_limit_id: mockId.rate_limits.new[1],
- client_id: mockValidClientsId,
- user_id: mockValidUsersId,
+ rate_limit_id: mockId.rate_limits.new[1] as RateLimitsId,
+ client_id: mockValidClientId,
+ user_id: mockValidUserId,
day: '2025-02-05',
count: 1,
last_modified: '2025-02-05T04:38:05.000Z'
@@ -124,8 +127,8 @@ describe('Unit-tests (repository), priority: entity RateLimits', () => {
let mockParam_entity: Partial;
beforeEach(() => {
mockParam_entity = {
- client_id: mockValidClientsId,
- user_id: mockValidUsersId,
+ client_id: mockValidClientId,
+ user_id: mockValidUserId,
day: mockDate,
count: 1,
last_modified: mockTimestamp
@@ -136,9 +139,9 @@ describe('Unit-tests (repository), priority: entity RateLimits', () => {
test('Return data for created entry, params: ', async () => {
const mockResult: RateLimitsResponseDTO = {
- rate_limit_id: mockId.rate_limits.valid[0],
- client_id: mockValidClientsId,
- user_id: mockValidUsersId,
+ rate_limit_id: mockValidRateLimitId,
+ client_id: mockValidClientId,
+ user_id: mockValidUserId,
day: mockDate,
count: 1,
last_modified: mockTimestamp
@@ -179,8 +182,8 @@ describe('Unit-tests (repository), priority: entity RateLimits', () => {
beforeEach(() => {
sql = 'UPDATE';
mockParam_dto = {
- client_id: mockValidClientsId,
- user_id: mockValidUsersId,
+ client_id: mockValidClientId,
+ user_id: mockValidUserId,
day: '2025-01-01',
last_modified: mockTimestamp
};
@@ -188,7 +191,7 @@ describe('Unit-tests (repository), priority: entity RateLimits', () => {
test('Return data of changed entry, params: valid ', async () => {
const mockResult: RateLimitsResponseDTO | null = {
- rate_limit_id: mockId.rate_limits.valid[0],
+ rate_limit_id:mockValidRateLimitId,
client_id: mockParam_dto.client_id,
user_id: mockParam_dto.user_id,
day: '2025-01-01',
@@ -209,8 +212,8 @@ describe('Unit-tests (repository), priority: entity RateLimits', () => {
test('Return null for non-existing entry, params: invalid ', async () => {
const mockParam_dto_null: RateLimitsUpdateDTO = {
- client_id: mockValidClientsId,
- user_id: mockValidUsersId,
+ client_id: mockValidClientId,
+ user_id: mockValidUserId,
day: '2025-01-01',
last_modified: mockTimestamp
};
@@ -233,8 +236,8 @@ describe('Unit-tests (repository), priority: entity RateLimits', () => {
test('Throw DBQueryErrorException by catch-block', async () => {
const mockParam_dto: RateLimitsUpdateDTO = {
- client_id: mockId.clients.invalid[0],
- user_id: mockId.users.invalid[0],
+ client_id: mockValidClientId,
+ user_id: mockValidUserId,
day: '2025-01-01',
last_modified: mockTimestamp
};
@@ -264,7 +267,7 @@ describe('Unit-tests (repository), priority: entity RateLimits', () => {
test('Return entries by day, params: ', async () => {
const mockResult: DemoLimits[] = [
{
- demo_limit_id: 2,
+ demo_limit_id: 2 as DemoLimitsId,
day: mockParam_dto.day,
count: 1,
last_modified: '2025-02-05T04:17:05.000Z'
@@ -314,7 +317,7 @@ describe('Unit-tests (repository), priority: entity RateLimits', () => {
test('Return data for created entry, params: >', async () => {
const mockResult: DemoLimitsResponseDTO = {
- demo_limit_id: 1,
+ demo_limit_id: 1 as DemoLimitsId,
day: mockDate,
count: 1,
last_modified: mockTimestamp
@@ -362,7 +365,7 @@ describe('Unit-tests (repository), priority: entity RateLimits', () => {
test('Return data of changed entry, params: valid ', async () => {
const mockResult: DemoLimitsResponseDTO | null = {
- demo_limit_id: 1,
+ demo_limit_id: 1 as DemoLimitsId,
day: '2025-01-01',
count: 1,
last_modified: mockTimestamp
diff --git a/backend/tests/unit-tests/repositories/tickets.repositories.test.ts b/backend/tests/unit-tests/repositories/tickets.repositories.test.ts
index e8465c8..7b7b90c 100644
--- a/backend/tests/unit-tests/repositories/tickets.repositories.test.ts
+++ b/backend/tests/unit-tests/repositories/tickets.repositories.test.ts
@@ -2,13 +2,15 @@ import { DBConnection } from "../../../src/configs/db";
import * as CommonUtils from "../../../src/utils/common.utils";
import * as MockUtils from "../../common.test-utils";
import { default as mockId } from "../../mock-data/id.mock-data.json";
-import { Tickets } from "../../../src/repositories/interfaces/tickets.entity.interface";
+import { Tickets, TicketsId } from "../../../src/repositories/interfaces/tickets.entity.interface";
import { DBQueryErrorException } from "../../../src/utils/exceptions/db.exception";
import { TicketStatus } from "../../../src/utils/enums/ticket-status.enum";
import ticketsRepository from "../../../src/repositories/tickets.repository";
import { TicketsFilterDTO, TicketsIntervalDTO, TicketsResponseDTO, TicketsResponseExtendedDTO, TicketsUpdateDTO } from "../../../src/dtos/tickets.dto";
import { TicketOption } from "../../../src/utils/enums/ticket-option.enum";
import { DeviceOption } from "../../../src/utils/enums/device-option.enum";
+import { UsersId } from "../../../src/repositories/interfaces/users.entity.interface";
+import { ClientsId } from "../../../src/repositories/interfaces/clients.entity.interface";
jest.mock("../../../src/configs/db", () => {
return {
@@ -18,11 +20,14 @@ jest.mock("../../../src/configs/db", () => {
}
});
+const mockValidTicketId = mockId.tickets.valid[0] as TicketsId;
+const mockValidClientId = mockId.clients.valid[0] as ClientsId;
+const mockValidUserId = mockId.users.valid[0] as UsersId;
const mockTimestamp = '2025-01-01T14:00:04.000Z';
const mockData: Tickets = {
- ticket_id: mockId.tickets.valid[0],
- client_id: mockId.clients.valid[0],
- user_id: mockId.users.valid[0],
+ ticket_id: mockValidTicketId,
+ client_id: mockValidClientId,
+ user_id: mockValidUserId,
status: TicketStatus.ISSUED,
option: TicketOption.SUPPORT,
title: 'test-title',
@@ -67,7 +72,7 @@ describe('Unit-tests (repository), priority: entity Tickets', () => {
})
test('Return null for non-existing entry, params: non-existing ', async () => {
- const mockParam_id = mockId.tickets.invalid[0];
+ const mockParam_id = mockId.tickets.invalid[0] as TicketsId;
const mockResult = null;
const mockClient = MockUtils.mapMockDbClient(mockResult);
const testFn = await ticketsRepository.findById(mockParam_id);
@@ -84,7 +89,7 @@ describe('Unit-tests (repository), priority: entity Tickets', () => {
describe('Testing invalid fn calls', () => {
test('Throw DBQueryErrorException by catch-block', async () => {
- const mockParam_id = mockId.tickets.invalid[0];
+ const mockParam_id = mockId.tickets.invalid[0] as TicketsId;
const mockErrorMsg = "DB ERROR ON SELECT QUERY";
const mockResult = null;
jest.spyOn(CommonUtils, "logError").mockReturnValue();
@@ -108,9 +113,9 @@ describe('Unit-tests (repository), priority: entity Tickets', () => {
test('Return data for multiple existing entries', async () => {
const mockData_entry0: TicketsResponseDTO = structuredClone(mockData);
const mockData_entry1: TicketsResponseDTO = {
- ticket_id: 'another_valid_tickets_test_id',
- client_id: 'another_valid_clients_test_id',
- user_id: 'another_valid_users_test_id',
+ ticket_id: mockId.tickets.valid[1] as TicketsId,
+ client_id: mockId.clients.valid[1] as ClientsId,
+ user_id: mockId.users.valid[1] as UsersId,
status: TicketStatus.ACTIVE,
option: TicketOption.SUPPORT,
title: 'another-test-title',
@@ -170,26 +175,22 @@ describe('Unit-tests (repository), priority: entity Tickets', () => {
let sql: string;
let mockTimestamp_now: Date;
- let mockClientId: string;
- let mockUserId: string;
beforeEach(() => {
sql = `SELECT`;
mockTimestamp_now = new Date('2025-01-01T14:01:50.000Z');
- mockClientId = mockId.clients.valid[0];
- mockUserId = mockId.users.valid[0];
});
test('Return data for existing entry, params: ', async () => {
const mockParam_dto: TicketsIntervalDTO = {
- client_id: mockClientId,
+ client_id: mockValidClientId,
intervalTime: '1 minute'
};
const mockResult: TicketsResponseDTO[] = [
mockData,
{
- ticket_id: 'another_valid_tickets_id',
- client_id: mockClientId,
- user_id: 'another_valid_users_id',
+ ticket_id: mockId.tickets.valid[1] as TicketsId,
+ client_id: mockValidClientId,
+ user_id: mockId.users.valid[1] as UsersId,
status: TicketStatus.ACTIVE,
option: TicketOption.SUPPORT,
title: 'valid-test-title',
@@ -211,21 +212,21 @@ describe('Unit-tests (repository), priority: entity Tickets', () => {
expect(DBConnection.getInstance).toHaveBeenCalled();
expect(mockClient.query).toHaveBeenCalledWith(
expect.stringContaining(sql),
- expect.arrayContaining([mockClientId, mockTimestamp_now, mockParam_dto.intervalTime])
+ expect.arrayContaining([mockValidClientId, mockTimestamp_now, mockParam_dto.intervalTime])
);
})
test('Return data for existing entry, params: ', async () => {
const mockParam_dto: TicketsIntervalDTO = {
- user_id: mockUserId,
+ user_id: mockValidUserId,
intervalTime: '1 minute'
};
const mockResult: TicketsResponseDTO[] = [
mockData,
{
- ticket_id: 'another_valid_tickets_id',
- client_id: 'another_valid_client_id',
- user_id: mockUserId,
+ ticket_id: mockId.tickets.valid[1] as TicketsId,
+ client_id: mockId.clients.valid[1] as ClientsId,
+ user_id: mockValidUserId,
status: TicketStatus.ACTIVE,
option: TicketOption.SUPPORT,
title: 'valid-test-title',
@@ -247,13 +248,13 @@ describe('Unit-tests (repository), priority: entity Tickets', () => {
expect(DBConnection.getInstance).toHaveBeenCalled();
expect(mockClient.query).toHaveBeenCalledWith(
expect.stringContaining(sql),
- expect.arrayContaining([mockUserId, mockTimestamp_now, mockParam_dto.intervalTime])
+ expect.arrayContaining([mockValidUserId, mockTimestamp_now, mockParam_dto.intervalTime])
);
})
test('Return null for existing entry beyond time interval', async () => {
const mockParam_dto: TicketsIntervalDTO = {
- client_id: mockClientId,
+ client_id: mockValidClientId,
intervalTime: '1 minute'
};
const mockResult = null;
@@ -266,7 +267,7 @@ describe('Unit-tests (repository), priority: entity Tickets', () => {
expect(DBConnection.getInstance).toHaveBeenCalled();
expect(mockClient.query).toHaveBeenCalledWith(
expect.stringContaining(sql),
- expect.arrayContaining([mockClientId, mockTimestamp_now, mockParam_dto.intervalTime])
+ expect.arrayContaining([mockValidClientId, mockTimestamp_now, mockParam_dto.intervalTime])
);
})
})
@@ -275,7 +276,7 @@ describe('Unit-tests (repository), priority: entity Tickets', () => {
test('Throw DBQueryErrorException by catch-block', async () => {
const mockParam_dto = {
- user_id: mockId.users.valid[0],
+ user_id: mockValidUserId,
intervalTime: '1 minute'
};
const mockErrorMsg = "DB ERROR ON SELECT QUERY";
@@ -317,7 +318,9 @@ describe('Unit-tests (repository), priority: entity Tickets', () => {
})
test('Return null for non-existing entry, params: non-existing ', async () => {
- const mockParam_dto = { user_id: ['non-existing_users_test_id_0', 'non-existing_users_test_id_1'] };
+ const mockParam_dto = {
+ user_id: [mockId.users.invalid[0], mockId.users.invalid[1]] as UsersId[]
+ };
const mockValues = mockParam_dto.user_id;
const mockResult: Tickets[] | null = null;
@@ -351,9 +354,9 @@ describe('Unit-tests (repository), priority: entity Tickets', () => {
describe('Database tests table , priority: fn create()', () => {
const mockParam_entity: Tickets = {
- ticket_id: mockId.tickets.new[0],
- client_id: mockId.clients.valid[0],
- user_id: mockId.users.valid[0],
+ ticket_id: mockId.tickets.new[0] as TicketsId,
+ client_id: mockValidClientId,
+ user_id: mockValidUserId,
status: TicketStatus.ISSUED,
option: TicketOption.SUPPORT,
title: 'new-test-title',
@@ -420,7 +423,7 @@ describe('Unit-tests (repository), priority: entity Tickets', () => {
describe('Testing valid fn calls', () => {
test('Return data of changed entry, params: valid ', async () => {
- const mockParam_id = mockData.user_id;
+ const mockParam_id = mockData.ticket_id;
const mockValues: any[] = Object.values(mockParam_dto).map(value => value);
mockValues.push(mockParam_id);
const mockResult: Tickets = structuredClone(mockData);
@@ -438,7 +441,7 @@ describe('Unit-tests (repository), priority: entity Tickets', () => {
})
test('Return null for non-existing entry, params: invalid ', async () => {
- const mockParam_id = mockId.tickets.invalid[0];
+ const mockParam_id = mockId.tickets.invalid[0] as TicketsId;
const mockValues: any[] = Object.values(mockParam_dto).map(value => value);
mockValues.push(mockParam_id);
@@ -458,7 +461,7 @@ describe('Unit-tests (repository), priority: entity Tickets', () => {
describe('Testing invalid fn calls', () => {
test('Throw DBQueryErrorException by catch-block', async () => {
- const mockParam_id = mockId.tickets.invalid[0];
+ const mockParam_id = mockId.tickets.invalid[0] as TicketsId;
const mockErrorMsg = "DB ERROR ON UPDATE QUERY";
const mockResult = null;
jest.spyOn(CommonUtils, "logError").mockReturnValue();
@@ -495,7 +498,7 @@ describe('Unit-tests (repository), priority: entity Tickets', () => {
})
test('Return false for non-existing entry, params: invalid ', async () => {
- const mockParam_id = mockId.tickets.invalid[0];
+ const mockParam_id = mockId.tickets.invalid[0] as TicketsId;
const mockResult = false;
const mockClient = MockUtils.mapMockDbClient(mockResult);
const testFn = await ticketsRepository.delete(mockParam_id);
@@ -512,7 +515,7 @@ describe('Unit-tests (repository), priority: entity Tickets', () => {
describe('Testing invalid fn calls', () => {
test('Throw DBQueryErrorException by catch-block', async () => {
- const mockParam_id = mockId.tickets.invalid[0];
+ const mockParam_id = mockId.tickets.invalid[0] as TicketsId;
const mockErrorMsg = "DB ERROR ON DELETE QUERY";
const mockResult = null;
jest.spyOn(CommonUtils, "logError").mockReturnValue();
diff --git a/backend/tests/unit-tests/repositories/users.repositories.test.ts b/backend/tests/unit-tests/repositories/users.repositories.test.ts
index e14665a..e5b9385 100644
--- a/backend/tests/unit-tests/repositories/users.repositories.test.ts
+++ b/backend/tests/unit-tests/repositories/users.repositories.test.ts
@@ -2,7 +2,7 @@ import { DBConnection } from "../../../src/configs/db";
import * as CommonUtils from "../../../src/utils/common.utils";
import * as MockUtils from "../../common.test-utils";
import { default as mockId } from "../../mock-data/id.mock-data.json";
-import { Users } from "../../../src/repositories/interfaces/users.entity.interface";
+import { Users, UsersId } from "../../../src/repositories/interfaces/users.entity.interface";
import { UserStatus } from "../../../src/utils/enums/user-status.enum";
import usersRepository from "../../../src/repositories/users.repository";
import { UsersUpdateDTO, UsersFlagUpdateDTO } from "../../../src/dtos/users.dto";
@@ -17,9 +17,10 @@ jest.mock("../../../src/configs/db", () => {
}
});
+const mockValidUserId = mockId.users.valid[0] as UsersId;
const mockTimestamp = '2025-01-03T14:00:03.000Z';
const mockData: Users = {
- user_id: mockId.users.valid[0],
+ user_id: mockValidUserId,
email: 'user@test.com',
status: UserStatus.ACTIVE,
flag: null,
@@ -41,7 +42,7 @@ describe('Unit-tests (repository), priority: entity Users', () => {
});
test('Return data for existing entry, params: valid ', async () => {
- const mockParam_id = mockId.users.valid[0];
+ const mockParam_id = mockValidUserId;
const mockResult: Users | null = structuredClone(mockData);
const mockClient = MockUtils.mapMockDbClient(mockResult);
@@ -56,7 +57,7 @@ describe('Unit-tests (repository), priority: entity Users', () => {
})
test('Return null for non-existing entry, params: non-existing ', async () => {
- const mockParam_id = mockId.users.invalid[0];
+ const mockParam_id = mockId.users.invalid[0] as UsersId;
const mockResult: Users | null = null;
const mockClient = MockUtils.mapMockDbClient(mockResult);
@@ -74,7 +75,7 @@ describe('Unit-tests (repository), priority: entity Users', () => {
describe('Testing invalid fn calls', () => {
test('Throw DBQueryErrorException by catch-block', async () => {
- const mockParam_id = mockId.users.invalid[0];
+ const mockParam_id = mockId.users.invalid[0] as UsersId;
const mockErrorMsg = "DB ERROR ON SELECT QUERY";
const mockResult: Users | null = null;
@@ -152,7 +153,7 @@ describe('Unit-tests (repository), priority: entity Users', () => {
test('Return data for multiple existing entries', async () => {
const mockData_entry0 = structuredClone(mockData);
const mockData_entry1 = structuredClone(mockData_entry0);
- mockData_entry1['user_id'] = 'another_valid_users_test_id';
+ mockData_entry1['user_id'] = 'another_valid_users_test_id' as UsersId;
mockData_entry1['email'] = 'user1@test.com';
const mockResult: Users[] = [mockData_entry0, mockData_entry1];
@@ -258,7 +259,7 @@ describe('Unit-tests (repository), priority: entity Users', () => {
beforeEach(() => {
sql = 'INSERT';
mockParam_entity = {
- user_id: mockId.users.valid[0],
+ user_id: mockValidUserId,
email: 'new-user@test.com',
status: UserStatus.ACTIVE,
flag: null,
@@ -315,7 +316,7 @@ describe('Unit-tests (repository), priority: entity Users', () => {
describe('Testing valid fn calls', () => {
test('Return data of changed entry, params: valid ', async () => {
- const mockParam_id = mockId.users.valid[0];
+ const mockParam_id = mockValidUserId;
const mockValues = Object.values(mockParam_dto).map(value => value);
mockValues.push(mockParam_id);
const mockResult: Users = structuredClone(mockData);
@@ -333,7 +334,7 @@ describe('Unit-tests (repository), priority: entity Users', () => {
})
test('Return null for no entries, params: non-existing ', async () => {
- const mockParam_id = mockId.users.invalid[0];
+ const mockParam_id = mockId.users.invalid[0] as UsersId;
const mockValues = Object.values(mockParam_dto).map(value => value);
mockValues.push(mockParam_id);
@@ -354,7 +355,7 @@ describe('Unit-tests (repository), priority: entity Users', () => {
describe('Testing invalid fn calls', () => {
test('Throw DBQueryErrorException by catch-block', async () => {
- const mockParam_id = mockId.users.invalid[0];
+ const mockParam_id = mockId.users.invalid[0] as UsersId;
const mockErrorMsg = "DB ERROR ON UPDATE QUERY";
const mockResult = null;
jest.spyOn(CommonUtils, "logError").mockReturnValue();
@@ -381,7 +382,7 @@ describe('Unit-tests (repository), priority: entity Users', () => {
describe('Testing valid fn calls', () => {
test('Return data of changed entry, params: valid ', async () => {
- const mockParam_id = mockId.users.valid[0];
+ const mockParam_id = mockValidUserId;
const mockValues = [mockParam_dto.flag, mockParam_dto.last_modified, mockParam_id];
const mockResult: Users = structuredClone(mockData);
@@ -399,7 +400,7 @@ describe('Unit-tests (repository), priority: entity Users', () => {
})
test('Return null for no entries, params: non-existing ', async () => {
- const mockParam_id = mockId.users.invalid[0];
+ const mockParam_id = mockId.users.invalid[0] as UsersId;
const mockValues = [mockParam_dto.flag, mockParam_dto.last_modified, mockParam_id];
const mockResult = null;
@@ -419,7 +420,7 @@ describe('Unit-tests (repository), priority: entity Users', () => {
describe('Testing invalid fn calls', () => {
test('Throw DBQueryErrorException by catch-block', async () => {
- const mockParam_id = mockId.users.invalid[0];
+ const mockParam_id = mockId.users.invalid[0] as UsersId;
const mockErrorMsg = "DB ERROR ON UPDATE QUERY";
const mockResult = null;
jest.spyOn(CommonUtils, "logError").mockReturnValue();
diff --git a/backend/tests/unit-tests/utils/common.utils.test.ts b/backend/tests/unit-tests/utils/common.utils.test.ts
index d8c197b..07a5b5e 100644
--- a/backend/tests/unit-tests/utils/common.utils.test.ts
+++ b/backend/tests/unit-tests/utils/common.utils.test.ts
@@ -1,4 +1,4 @@
-import { Users } from "../../../src/repositories/interfaces/users.entity.interface";
+import { Users, UsersId } from "../../../src/repositories/interfaces/users.entity.interface";
import * as CommonUtils from "../../../src/utils/common.utils";
import { Flag } from "../../../src/utils/enums/flag.enum";
import { TicketStatus } from "../../../src/utils/enums/ticket-status.enum";
@@ -9,6 +9,8 @@ import { default as mockId } from "../../mock-data/id.mock-data.json";
describe('Unit-tests (utils), priority: synonym CommonUtils', () => {
+ const mockValidUserId = mockId.users.valid[0] as UsersId;
+
describe('Testing valid fn calls', () => {
test('Fn mapKeyToHash()', () => {
@@ -54,7 +56,7 @@ describe('Unit-tests (utils), priority: synonym CommonUtils', () => {
test('Fn mapObjTimestamps()', () => {
const mockParam_data: Users = {
- user_id: mockId.users.valid[0],
+ user_id: mockValidUserId,
email: 'user@test.com',
status: UserStatus.ACTIVE,
flag: null,
@@ -65,7 +67,7 @@ describe('Unit-tests (utils), priority: synonym CommonUtils', () => {
const testFn = CommonUtils.mapObjTimestamps(mockParam_data, mockParam_timeMapTargets);
const expectResult: Users = {
- user_id: mockId.users.valid[0],
+ user_id: mockValidUserId,
email: 'user@test.com',
status: UserStatus.ACTIVE,
flag: null,
@@ -79,7 +81,7 @@ describe('Unit-tests (utils), priority: synonym CommonUtils', () => {
test('Fn mapArrayTimestamps()', () => {
const mockParam_data: Users[] = [
{
- user_id: mockId.users.valid[0],
+ user_id: mockValidUserId,
email: 'user0@test.com',
status: UserStatus.ACTIVE,
flag: null,
@@ -87,7 +89,7 @@ describe('Unit-tests (utils), priority: synonym CommonUtils', () => {
created_on: '2025-01-01 14:00:03.000+01'
},
{
- user_id: mockId.users.new[0],
+ user_id: mockId.users.new[0] as UsersId,
email: 'user1@test.com',
status: UserStatus.ACTIVE,
flag: null,
@@ -100,7 +102,7 @@ describe('Unit-tests (utils), priority: synonym CommonUtils', () => {
const testFn = CommonUtils.mapArrayTimestamps(mockParam_data, mockParam_timeMapTargets);
const expectResult: Users[] = [
{
- user_id: mockId.users.valid[0],
+ user_id: mockValidUserId,
email: 'user0@test.com',
status: UserStatus.ACTIVE,
flag: null,
@@ -108,7 +110,7 @@ describe('Unit-tests (utils), priority: synonym CommonUtils', () => {
created_on: '2025-01-01T13:00:03.000Z'
},
{
- user_id: mockId.users.new[0],
+ user_id: mockId.users.new[0] as UsersId,
email: 'user1@test.com',
status: UserStatus.ACTIVE,
flag: null,
diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md
index a020ec8..4d6e52d 100644
--- a/docs/CHANGELOG.md
+++ b/docs/CHANGELOG.md
@@ -4,9 +4,19 @@
+### $\textsf{\color{skyblue}2026/03/09}$
+
+$\textsf{[v1.4.1\ =>\ {\textbf{\color{brown}v1.4.4}]}}$ app
+- $\textsf{\color{orange}Patch:}$ Updated:
+ + entity ID's are using now nominal types instead basic string|number.
+ + some model functions are renamed to keep consistency and improve readability.
+ + some api routes have been shortened to keep consistency and improve readability.
+
+
+
### $\textsf{\color{skyblue}2026/03/07}$
-$\textsf{[v1.3.5\ =>\ {\textbf{\color{brown}v1.4.1}]}}$ app
+$\textsf{[v1.3.5\ =>\ v1.4.1]}$ app
$\textsf{[v1.5.4\ =>\ {\textbf{\color{brown}v1.6.0}]}}$ database
- $\textsf{\color{teal}Addition:}$ Added api route + logic for Feedback/FeedbackRating to add new feedback/rating or get current average rating value.
- $\textsf{\color{orange}Patch:}$ Updated:
diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md
new file mode 100644
index 0000000..d63063c
--- /dev/null
+++ b/docs/CONFIGURATION.md
@@ -0,0 +1,12 @@
+## 🔑 $\textsf{\color{salmon}.env Setup}$
+
+### Additional information to set `.env` up correctly :
+
+| Key | Information |
+|-----|-------------|
+| SECRET_ADMIN_KEY | free to create yourself |
+| SECRET_DB_TEST_.... | use default data for all DB_TEST vars or [see setup](../backend/tests/db-migrations.setup.ts) |
+| SECRET_TEST_APIKEY_RAW | use RAW from generateApiKeyObj() [see model](../backend/src/models/clients.model.ts) |
+| SECRET_TEST_APIKEY_HASH | use HASH from same function as RAW |
+| SECRET_EMAIL_PASS | password from SENDER email |
+
diff --git a/docs/DEVOPS.md b/docs/DEVOPS.md
index 7bee7a7..1fe9c09 100644
--- a/docs/DEVOPS.md
+++ b/docs/DEVOPS.md
@@ -1,7 +1,4 @@
-## How to handle DevOps (Docker / Jenkins)?
-
-### `Docker`
-
+## 🐋 $\textsf{\color{salmon}How to run app in Docker container}$
Build image, volumes and container from docker-compose.yml of current path and start all (Whenever code-changes are registered, a new image will be built, otherwise the old last image will be restored.):
diff --git a/docs/GLOSSARY.md b/docs/GLOSSARY.md
index 35492e9..096a567 100644
--- a/docs/GLOSSARY.md
+++ b/docs/GLOSSARY.md
@@ -94,3 +94,4 @@ Basic description: `...` [specific always sing
|------------------------|-----------------------------|------------------------------|
| S_ID | Service Identification | 'artdv', 'tava' |
| Desc | Description | -- |
+| Ta | Transaction | short form for fn naming |
diff --git a/docs/MIGRATION.md b/docs/MIGRATION.md
index cfb2c82..57cf22e 100644
--- a/docs/MIGRATION.md
+++ b/docs/MIGRATION.md
@@ -1,11 +1,11 @@
-## How to migrate?
+## 🆕 $\textsf{\color{salmon}How to add new migration}$
-Create a new migration file (name scheme: -update.migrations).
This will be saved automatically at [migrations](../../migrations/).
+Create a new migration file (ignore or follow name scheme: -update.migrations).
+This will be saved automatically at [migrations](../../migrations/).
```sh
-npm run migrate create --.migrations
+npx node-pg-migrate create --.migrations
```
-
Adapt new migration file on `up` (target changes) and `down` (reset to current state).
```sh
const shorthands = undefined;
@@ -20,39 +20,34 @@ module.exports = {
module.exports = {
shorthands,
down: (pgm) => {
- // reset to current state
+ // reset to original state
}
}
```
-Since a central db is in use for this project, the migration can be executed locally.
+
+
+## ▶️ $\textsf{\color{salmon}How to run migrations}$
+
+### Since a central db is in use for this project, the migration can be executed locally.
-Set connection string as environment variable by the following command in powershell
-local database:
+
+
+Set connection string as environment variable in powershell for `local database`:
```sh
path> $env:DATABASE_URL = "postgresql://:@:/"
```
-hosted database (like Neon):
+... or for `hosted database` (like Neon):
```sh
path> $env:DATABASE_URL = "postgresql://:@/?sslmode=require"
```
-(direction: 'up' or 'down')
-for specific number of migrations (going from youngest to oldest):
+To run migration (direction: 'up' or 'down')
+specify number of migrations (going from youngest to oldest) or ignore `count` to run all:
```sh
-path> npx node-pg-migrate #==> npx node-pg-migrate down 9999
-or
-path> npm run migrate-
-```
-
-or, running all migrations including specified one (up/down, migration file `without .js`):
-
-```sh
-path> $env:DATABASE_URL =