From 1a122ef1af4b61f60f455d053a9d08fd07c01182 Mon Sep 17 00:00:00 2001 From: Luca Date: Fri, 2 Feb 2024 17:14:40 +0100 Subject: [PATCH 01/86] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 1bd07ee4..2ea6fba0 100644 --- a/README.md +++ b/README.md @@ -35,3 +35,5 @@ Whenever you make any change to the API (e.g. adding a new route, changing the p [TODO]: TODO + +delete me later From 724ff13a496f965c1ff8936d67df1a4954a12d7a Mon Sep 17 00:00:00 2001 From: Luca Date: Fri, 2 Feb 2024 17:15:14 +0100 Subject: [PATCH 02/86] Update README.md --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 2ea6fba0..1bd07ee4 100644 --- a/README.md +++ b/README.md @@ -35,5 +35,3 @@ Whenever you make any change to the API (e.g. adding a new route, changing the p [TODO]: TODO - -delete me later From 3640cb2d33b027c6db7deded9c51f5efb60d7682 Mon Sep 17 00:00:00 2001 From: Luca Date: Fri, 2 Feb 2024 17:22:55 +0100 Subject: [PATCH 03/86] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1bd07ee4..bf18ef7c 100644 --- a/README.md +++ b/README.md @@ -35,3 +35,4 @@ Whenever you make any change to the API (e.g. adding a new route, changing the p [TODO]: TODO +aa From a26db63c65749b17766e3c819b356948eb3252e8 Mon Sep 17 00:00:00 2001 From: Luca Terrazzan Date: Fri, 2 Feb 2024 17:26:08 +0100 Subject: [PATCH 04/86] updated app name --- fly.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fly.toml b/fly.toml index eac058e7..f9aaa63a 100644 --- a/fly.toml +++ b/fly.toml @@ -1,6 +1,6 @@ # fly.toml file generated for team-dev-backend-api on 2022-11-30T11:30:34Z -app = "team-dev-backend-api" +app = "team-dev-server-c10" kill_signal = "SIGINT" kill_timeout = 5 processes = [] From 1bf3efebc02cfdb6d830f0611fcc66a1246fd7bd Mon Sep 17 00:00:00 2001 From: auenc Date: Fri, 2 Feb 2024 16:31:17 +0000 Subject: [PATCH 05/86] change to force pre-commit --- .husky/pre-commit | 0 README.md | 1 + 2 files changed, 1 insertion(+) mode change 100644 => 100755 .husky/pre-commit diff --git a/.husky/pre-commit b/.husky/pre-commit old mode 100644 new mode 100755 diff --git a/README.md b/README.md index bf18ef7c..00c5ce7f 100644 --- a/README.md +++ b/README.md @@ -36,3 +36,4 @@ Whenever you make any change to the API (e.g. adding a new route, changing the p [TODO]: TODO aa +bb \ No newline at end of file From b5662c2664c44e6a6b519300cef74daf3c057bf5 Mon Sep 17 00:00:00 2001 From: auenc Date: Fri, 2 Feb 2024 16:39:22 +0000 Subject: [PATCH 06/86] updated prepare script --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 41e19e5e..72d40696 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "scripts": { "start": "node src/index.js", "dev": "nodemon src/index.js", - "prepare": "husky install", + "prepare": "husky", "db-reset": "prisma migrate reset" }, "prisma": { From 675acf49f170603231c73ca8c970bba80b330177 Mon Sep 17 00:00:00 2001 From: Luca Terrazzan Date: Fri, 2 Feb 2024 17:41:31 +0100 Subject: [PATCH 07/86] automatic flyio changes to config --- fly.toml | 71 ++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 43 insertions(+), 28 deletions(-) diff --git a/fly.toml b/fly.toml index f9aaa63a..18c19533 100644 --- a/fly.toml +++ b/fly.toml @@ -1,42 +1,57 @@ -# fly.toml file generated for team-dev-backend-api on 2022-11-30T11:30:34Z +# fly.toml app configuration file generated for boolean-team-dev-server on 2024-02-02T17:36:23+01:00 +# +# See https://fly.io/docs/reference/configuration/ for information about how to use this file. +# -app = "team-dev-server-c10" -kill_signal = "SIGINT" -kill_timeout = 5 -processes = [] - -[env] - PORT = "8080" +app = 'boolean-team-dev-server' +primary_region = 'ams' +kill_signal = 'SIGINT' +kill_timeout = '5s' [experimental] - allowed_public_ports = [] auto_rollback = true +[build] + [deploy] - release_command = "npx prisma migrate deploy" + release_command = 'npx prisma migrate deploy' + +[env] + PORT = '8080' + +[http_service] + internal_port = 3000 + force_https = true + auto_stop_machines = true + auto_start_machines = true + min_machines_running = 0 + processes = ['app'] [[services]] - http_checks = [] + protocol = 'tcp' internal_port = 8080 - processes = ["app"] - protocol = "tcp" - script_checks = [] - [services.concurrency] - hard_limit = 25 - soft_limit = 20 - type = "connections" + processes = ['app'] - [[services.ports]] - force_https = true - handlers = ["http"] +[[services.ports]] port = 80 + handlers = ['http'] + force_https = true - [[services.ports]] - handlers = ["tls", "http"] +[[services.ports]] port = 443 + handlers = ['tls', 'http'] + + [services.concurrency] + type = 'connections' + hard_limit = 25 + soft_limit = 20 + +[[services.tcp_checks]] + interval = '15s' + timeout = '2s' + grace_period = '1s' - [[services.tcp_checks]] - grace_period = "1s" - interval = "15s" - restart_limit = 0 - timeout = "2s" +[[vm]] + cpu_kind = 'shared' + cpus = 1 + memory_mb = 1024 From 2bdae4632b1bc83805546b49348127a49a2a3db6 Mon Sep 17 00:00:00 2001 From: Luca Terrazzan Date: Fri, 2 Feb 2024 17:43:03 +0100 Subject: [PATCH 08/86] don't specify vm --- fly.toml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/fly.toml b/fly.toml index 18c19533..3ac27a0b 100644 --- a/fly.toml +++ b/fly.toml @@ -50,8 +50,3 @@ kill_timeout = '5s' interval = '15s' timeout = '2s' grace_period = '1s' - -[[vm]] - cpu_kind = 'shared' - cpus = 1 - memory_mb = 1024 From 96f50f7bb6149836e4ab1e03fa090dfb4c12bb35 Mon Sep 17 00:00:00 2001 From: auenc Date: Fri, 2 Feb 2024 16:46:28 +0000 Subject: [PATCH 09/86] chore: update husky version --- package-lock.json | 18 +++++++++--------- package.json | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index 044145e5..5c51e1bd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,7 +27,7 @@ "eslint-plugin-node": "^11.1.0", "eslint-plugin-prettier": "^4.0.0", "eslint-plugin-promise": "^5.1.0", - "husky": "^7.0.4", + "husky": "^9.0.10", "nodemon": "^2.0.15", "prettier": "^2.6.2", "prisma": "^3.12.0" @@ -1766,15 +1766,15 @@ } }, "node_modules/husky": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/husky/-/husky-7.0.4.tgz", - "integrity": "sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==", + "version": "9.0.10", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.0.10.tgz", + "integrity": "sha512-TQGNknoiy6bURzIO77pPRu+XHi6zI7T93rX+QnJsoYFf3xdjKOur+IlfqzJGMHIK/wXrLg+GsvMs8Op7vI2jVA==", "dev": true, "bin": { - "husky": "lib/bin.js" + "husky": "bin.mjs" }, "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/typicode" @@ -4894,9 +4894,9 @@ } }, "husky": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/husky/-/husky-7.0.4.tgz", - "integrity": "sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==", + "version": "9.0.10", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.0.10.tgz", + "integrity": "sha512-TQGNknoiy6bURzIO77pPRu+XHi6zI7T93rX+QnJsoYFf3xdjKOur+IlfqzJGMHIK/wXrLg+GsvMs8Op7vI2jVA==", "dev": true }, "iconv-lite": { diff --git a/package.json b/package.json index 72d40696..61b6e3bd 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "eslint-plugin-node": "^11.1.0", "eslint-plugin-prettier": "^4.0.0", "eslint-plugin-promise": "^5.1.0", - "husky": "^7.0.4", + "husky": "^9.0.10", "nodemon": "^2.0.15", "prettier": "^2.6.2", "prisma": "^3.12.0" From c23b4f04cc4f4ab476f82c0463f454ae7f0fe43a Mon Sep 17 00:00:00 2001 From: Luca Terrazzan Date: Fri, 2 Feb 2024 17:56:32 +0100 Subject: [PATCH 10/86] extrapolate migration command to dockerfile --- .github/workflows/ci.yml | 5 +++-- Dockerfile | 2 ++ fly.toml | 3 --- package.json | 7 +++++-- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 56cddc0a..efef13b4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,5 +13,6 @@ jobs: with: node-version: 'lts/*' - run: npm ci - - run: npx eslint src - - run: npx prisma migrate reset --force --skip-seed + - run: npm run lint + - run: npm run test + - run: npm run db-reset diff --git a/Dockerfile b/Dockerfile index b0c8ba77..31f6c1b0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,4 +33,6 @@ WORKDIR /app ENV NODE_ENV production ENV PATH /root/.volta/bin:$PATH +RUN npm run migrate + CMD [ "npm", "run", "start" ] diff --git a/fly.toml b/fly.toml index 3ac27a0b..7bd9db6a 100644 --- a/fly.toml +++ b/fly.toml @@ -13,9 +13,6 @@ kill_timeout = '5s' [build] -[deploy] - release_command = 'npx prisma migrate deploy' - [env] PORT = '8080' diff --git a/package.json b/package.json index 72d40696..9887eac4 100644 --- a/package.json +++ b/package.json @@ -7,8 +7,11 @@ "scripts": { "start": "node src/index.js", "dev": "nodemon src/index.js", - "prepare": "husky", - "db-reset": "prisma migrate reset" + "prepare": "npx husky install", + "lint": "eslint src", + "test": "jest", + "migrate": "prisma migrate deploy", + "db-reset": "prisma migrate reset --force --skip-seed" }, "prisma": { "seed": "node prisma/seed.js" From e02040832b6c4c9820418f2a793ed8f425c02c54 Mon Sep 17 00:00:00 2001 From: Luca Terrazzan Date: Fri, 2 Feb 2024 18:00:57 +0100 Subject: [PATCH 11/86] downgrade flytoml --- fly.toml | 57 +++++++++++++++++++++++++------------------------------- 1 file changed, 25 insertions(+), 32 deletions(-) diff --git a/fly.toml b/fly.toml index 7bd9db6a..09f71d04 100644 --- a/fly.toml +++ b/fly.toml @@ -4,46 +4,39 @@ # app = 'boolean-team-dev-server' -primary_region = 'ams' -kill_signal = 'SIGINT' -kill_timeout = '5s' - -[experimental] - auto_rollback = true - -[build] +kill_signal = "SIGINT" +kill_timeout = 5 +primary_region = "lhr" [env] - PORT = '8080' + PORT = "8080" -[http_service] - internal_port = 3000 - force_https = true - auto_stop_machines = true - auto_start_machines = true - min_machines_running = 0 - processes = ['app'] +[experimental] + allowed_public_ports = [] + auto_rollback = true [[services]] - protocol = 'tcp' + http_checks = [] internal_port = 8080 - processes = ['app'] + processes = ["app"] + protocol = "tcp" + script_checks = [] + [services.concurrency] + hard_limit = 25 + soft_limit = 20 + type = "connections" -[[services.ports]] - port = 80 - handlers = ['http'] + [[services.ports]] force_https = true + handlers = ["http"] + port = 80 -[[services.ports]] + [[services.ports]] + handlers = ["tls", "http"] port = 443 - handlers = ['tls', 'http'] - - [services.concurrency] - type = 'connections' - hard_limit = 25 - soft_limit = 20 -[[services.tcp_checks]] - interval = '15s' - timeout = '2s' - grace_period = '1s' + [[services.tcp_checks]] + grace_period = "1s" + interval = "15s" + restart_limit = 0 + timeout = "2s" From 1b691eb149fdf4a02ad5b793c2155fe3f8f093a6 Mon Sep 17 00:00:00 2001 From: Luca Terrazzan Date: Fri, 2 Feb 2024 18:08:39 +0100 Subject: [PATCH 12/86] remove test command --- .github/workflows/ci.yml | 1 - package.json | 1 - 2 files changed, 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index efef13b4..1add38cc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,5 +14,4 @@ jobs: node-version: 'lts/*' - run: npm ci - run: npm run lint - - run: npm run test - run: npm run db-reset diff --git a/package.json b/package.json index b64c229c..f63f47f5 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,6 @@ "dev": "nodemon src/index.js", "prepare": "npx husky install", "lint": "eslint src", - "test": "jest", "migrate": "prisma migrate deploy", "db-reset": "prisma migrate reset --force --skip-seed" }, From 9b1d2835bba3815bcc824b093bbebd49403667a7 Mon Sep 17 00:00:00 2001 From: auenc Date: Mon, 5 Feb 2024 12:15:11 +0000 Subject: [PATCH 13/86] chore: updating deployed api url --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 00c5ce7f..571cff38 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Once you have complete the above guide, continue to the steps below. ## API Spec [TODO]: -[Deployed API Spec](https://UPDATEME) +[Deployed API Spec](https://boolean-team-dev-server.fly.dev/api-docs/) The API Spec is hosted by the server itself (i.e. this project), and the view/page is generated automatically by the SwaggerUI libraryi. @@ -35,5 +35,3 @@ Whenever you make any change to the API (e.g. adding a new route, changing the p [TODO]: TODO -aa -bb \ No newline at end of file From bcb3f51ad4c6a246d756ed8f35fe2b10f1843bb6 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Tue, 6 Feb 2024 12:45:39 +0000 Subject: [PATCH 14/86] build: add updateAt and createdAt to schema --- prisma/schema.prisma | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 72ec5632..ae5083d0 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -26,6 +26,8 @@ model User { cohort Cohort? @relation(fields: [cohortId], references: [id]) posts Post[] deliveryLogs DeliveryLog[] + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt } model Profile { @@ -36,20 +38,26 @@ model Profile { lastName String bio String? githubUrl String? -} + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + } model Cohort { id Int @id @default(autoincrement()) users User[] deliveryLogs DeliveryLog[] -} + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + } model Post { id Int @id @default(autoincrement()) content String userId Int user User @relation(fields: [userId], references: [id]) -} + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + } model DeliveryLog { id Int @id @default(autoincrement()) @@ -59,11 +67,15 @@ model DeliveryLog { cohortId Int cohort Cohort @relation(fields: [cohortId], references: [id]) lines DeliveryLogLine[] -} + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + } model DeliveryLogLine { id Int @id @default(autoincrement()) content String logId Int log DeliveryLog @relation(fields: [logId], references: [id]) -} + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + } From c642cbd55afe19c9e83c93c6280875c090276d71 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Tue, 6 Feb 2024 12:48:13 +0000 Subject: [PATCH 15/86] build: add prisma migration for createdAt and updatedAt --- .../migration.sql | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 prisma/migrations/20240206124649_add_updated_at_and_created_at/migration.sql diff --git a/prisma/migrations/20240206124649_add_updated_at_and_created_at/migration.sql b/prisma/migrations/20240206124649_add_updated_at_and_created_at/migration.sql new file mode 100644 index 00000000..01c8d682 --- /dev/null +++ b/prisma/migrations/20240206124649_add_updated_at_and_created_at/migration.sql @@ -0,0 +1,34 @@ +/* + Warnings: + + - Added the required column `updatedAt` to the `Cohort` table without a default value. This is not possible if the table is not empty. + - Added the required column `updatedAt` to the `DeliveryLog` table without a default value. This is not possible if the table is not empty. + - Added the required column `updatedAt` to the `DeliveryLogLine` table without a default value. This is not possible if the table is not empty. + - Added the required column `updatedAt` to the `Post` table without a default value. This is not possible if the table is not empty. + - Added the required column `updatedAt` to the `Profile` table without a default value. This is not possible if the table is not empty. + - Added the required column `updatedAt` to the `User` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "Cohort" ADD COLUMN "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, +ADD COLUMN "updatedAt" TIMESTAMP(3) NOT NULL; + +-- AlterTable +ALTER TABLE "DeliveryLog" ADD COLUMN "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, +ADD COLUMN "updatedAt" TIMESTAMP(3) NOT NULL; + +-- AlterTable +ALTER TABLE "DeliveryLogLine" ADD COLUMN "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, +ADD COLUMN "updatedAt" TIMESTAMP(3) NOT NULL; + +-- AlterTable +ALTER TABLE "Post" ADD COLUMN "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, +ADD COLUMN "updatedAt" TIMESTAMP(3) NOT NULL; + +-- AlterTable +ALTER TABLE "Profile" ADD COLUMN "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, +ADD COLUMN "updatedAt" TIMESTAMP(3) NOT NULL; + +-- AlterTable +ALTER TABLE "User" ADD COLUMN "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, +ADD COLUMN "updatedAt" TIMESTAMP(3) NOT NULL; From 9e434042ca645ca754a37562b9993e0f2c17e23b Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Tue, 6 Feb 2024 13:24:35 +0000 Subject: [PATCH 16/86] feat: update create() to call createPost() --- src/controllers/post.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/controllers/post.js b/src/controllers/post.js index 7b168039..dba5dfd8 100644 --- a/src/controllers/post.js +++ b/src/controllers/post.js @@ -1,3 +1,4 @@ +import { createPost } from '../domain/post.js' import { sendDataResponse } from '../utils/responses.js' export const create = async (req, res) => { @@ -6,8 +7,8 @@ export const create = async (req, res) => { if (!content) { return sendDataResponse(res, 400, { content: 'Must provide content' }) } - - return sendDataResponse(res, 201, { post: { id: 1, content } }) + await createPost(content) + return sendDataResponse(res, 201, { post: { content } }) } export const getAll = async (req, res) => { From 42be8f63684ee15201852d89a71698493959e85e Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Tue, 6 Feb 2024 13:37:35 +0000 Subject: [PATCH 17/86] feat: createPost() --- src/domain/post.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/domain/post.js diff --git a/src/domain/post.js b/src/domain/post.js new file mode 100644 index 00000000..352e4d32 --- /dev/null +++ b/src/domain/post.js @@ -0,0 +1,18 @@ +import dbClient from '../utils/dbClient.js' + +export async function createPost(content, userId) { + const createdPost = await dbClient.post.create({ + data: { + content, + user: { + connect: { + id: userId + } + } + }, + include: { + user: true + } + }) + return createdPost +} From b2f39e98918fea7cb8ba7f7bfe7805488bbdb9d0 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Tue, 6 Feb 2024 13:38:23 +0000 Subject: [PATCH 18/86] fix: amend create() --- src/controllers/post.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/controllers/post.js b/src/controllers/post.js index dba5dfd8..16b61342 100644 --- a/src/controllers/post.js +++ b/src/controllers/post.js @@ -3,11 +3,12 @@ import { sendDataResponse } from '../utils/responses.js' export const create = async (req, res) => { const { content } = req.body + const userId = req.user.id if (!content) { return sendDataResponse(res, 400, { content: 'Must provide content' }) } - await createPost(content) + await createPost(content, userId) return sendDataResponse(res, 201, { post: { content } }) } From d69bcca9d6f0a1dc99653f0a480d2d7377389f68 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Tue, 6 Feb 2024 13:42:02 +0000 Subject: [PATCH 19/86] feat: getPosts() --- src/domain/post.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/domain/post.js b/src/domain/post.js index 352e4d32..15af78a4 100644 --- a/src/domain/post.js +++ b/src/domain/post.js @@ -16,3 +16,7 @@ export async function createPost(content, userId) { }) return createdPost } + +export async function getPosts() { + return await dbClient.post.findMany() +} From a9662738d784b0cc7a1eb17f78b9893823a669e4 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Tue, 6 Feb 2024 13:42:45 +0000 Subject: [PATCH 20/86] feat: update getAll() to call getPosts() --- src/controllers/post.js | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/src/controllers/post.js b/src/controllers/post.js index 16b61342..1eb843b4 100644 --- a/src/controllers/post.js +++ b/src/controllers/post.js @@ -1,4 +1,4 @@ -import { createPost } from '../domain/post.js' +import { createPost, getPosts } from '../domain/post.js' import { sendDataResponse } from '../utils/responses.js' export const create = async (req, res) => { @@ -13,18 +13,6 @@ export const create = async (req, res) => { } export const getAll = async (req, res) => { - return sendDataResponse(res, 200, { - posts: [ - { - id: 1, - content: 'Hello world!', - author: { ...req.user } - }, - { - id: 2, - content: 'Hello from the void!', - author: { ...req.user } - } - ] - }) + const posts = await getPosts() + return sendDataResponse(res, 200, { posts }) } From dcc6593b79dcf96f4c7e28c0081b94f0b4f78bea Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Tue, 6 Feb 2024 14:23:44 +0000 Subject: [PATCH 21/86] fix: return created post --- src/controllers/post.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/controllers/post.js b/src/controllers/post.js index 1eb843b4..035300e0 100644 --- a/src/controllers/post.js +++ b/src/controllers/post.js @@ -8,8 +8,9 @@ export const create = async (req, res) => { if (!content) { return sendDataResponse(res, 400, { content: 'Must provide content' }) } - await createPost(content, userId) - return sendDataResponse(res, 201, { post: { content } }) + + const post = await createPost(content, userId) + return sendDataResponse(res, 201, post) } export const getAll = async (req, res) => { From cdf6f478f9f7519c9e7af4f83e5b2379f747f63e Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Tue, 6 Feb 2024 14:26:50 +0000 Subject: [PATCH 22/86] fix: add try...catch --- src/controllers/post.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/controllers/post.js b/src/controllers/post.js index 035300e0..02d0a980 100644 --- a/src/controllers/post.js +++ b/src/controllers/post.js @@ -9,8 +9,12 @@ export const create = async (req, res) => { return sendDataResponse(res, 400, { content: 'Must provide content' }) } - const post = await createPost(content, userId) - return sendDataResponse(res, 201, post) + try { + const post = await createPost(content, userId) + return sendDataResponse(res, 201, post) + } catch (e) { + return sendDataResponse(res, 500, 'something went wrong') + } } export const getAll = async (req, res) => { From fdac52b735f837899a641ba7f49894b8c8332fce Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Tue, 6 Feb 2024 14:33:19 +0000 Subject: [PATCH 23/86] fix: add console.error --- src/controllers/post.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/controllers/post.js b/src/controllers/post.js index 02d0a980..b29f27c4 100644 --- a/src/controllers/post.js +++ b/src/controllers/post.js @@ -13,6 +13,7 @@ export const create = async (req, res) => { const post = await createPost(content, userId) return sendDataResponse(res, 201, post) } catch (e) { + console.error('error creating post', e.message) return sendDataResponse(res, 500, 'something went wrong') } } From 82ae5b3e44c247d0db0022bf2e35baf576ecd370 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Tue, 6 Feb 2024 15:26:39 +0000 Subject: [PATCH 24/86] fix: add mock author data to posts in getAll() --- src/controllers/post.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/controllers/post.js b/src/controllers/post.js index b29f27c4..ed261721 100644 --- a/src/controllers/post.js +++ b/src/controllers/post.js @@ -20,5 +20,12 @@ export const create = async (req, res) => { export const getAll = async (req, res) => { const posts = await getPosts() - return sendDataResponse(res, 200, { posts }) + const postlist = posts.map((post) => ({ + ...post, + author: { + firstName: 'Loza', + lastName: 'MockUser' + } + })) + return sendDataResponse(res, 200, { posts: postlist }) } From 48e4d7c36f6b9fc4594da6a4fd1035837edb71e3 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Tue, 6 Feb 2024 16:06:37 +0000 Subject: [PATCH 25/86] fix: use user.profile data instead of mock --- src/controllers/post.js | 24 +++++++++++++++++------- src/domain/post.js | 10 +++++++++- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/controllers/post.js b/src/controllers/post.js index ed261721..477c1490 100644 --- a/src/controllers/post.js +++ b/src/controllers/post.js @@ -20,12 +20,22 @@ export const create = async (req, res) => { export const getAll = async (req, res) => { const posts = await getPosts() - const postlist = posts.map((post) => ({ - ...post, - author: { - firstName: 'Loza', - lastName: 'MockUser' + const newPostsList = posts.map((post) => { + const author = post.user.profile + ? { + firstName: post.user.profile.firstName, + lastName: post.user.profile.lastName + } + : { firstName: 'unknown', lastName: 'unknown' } + + return { + id: post.id, + content: post.content, + createdAt: post.createdAt, + updatedAt: post.updatedAt, + userId: post.user.id, + author } - })) - return sendDataResponse(res, 200, { posts: postlist }) + }) + return sendDataResponse(res, 200, { posts: newPostsList }) } diff --git a/src/domain/post.js b/src/domain/post.js index 15af78a4..7bed8167 100644 --- a/src/domain/post.js +++ b/src/domain/post.js @@ -18,5 +18,13 @@ export async function createPost(content, userId) { } export async function getPosts() { - return await dbClient.post.findMany() + return await dbClient.post.findMany({ + include: { + user: { + include: { + profile: true + } + } + } + }) } From 811eff5f3dec48f983c1c07afa294e52e15d84e8 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Tue, 6 Feb 2024 16:19:55 +0000 Subject: [PATCH 26/86] build: test migration file --- prisma/migrations/20240206161737_test/migration.sql | 1 + 1 file changed, 1 insertion(+) create mode 100644 prisma/migrations/20240206161737_test/migration.sql diff --git a/prisma/migrations/20240206161737_test/migration.sql b/prisma/migrations/20240206161737_test/migration.sql new file mode 100644 index 00000000..af5102c8 --- /dev/null +++ b/prisma/migrations/20240206161737_test/migration.sql @@ -0,0 +1 @@ +-- This is an empty migration. \ No newline at end of file From fd7282d9ea6866b68bc691a768b56e255bef0b0d Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Tue, 6 Feb 2024 16:45:02 +0000 Subject: [PATCH 27/86] feat: move mapping into domain --- src/controllers/post.js | 19 +------------------ src/domain/post.js | 22 +++++++++++++++++++++- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/controllers/post.js b/src/controllers/post.js index 477c1490..b29f27c4 100644 --- a/src/controllers/post.js +++ b/src/controllers/post.js @@ -20,22 +20,5 @@ export const create = async (req, res) => { export const getAll = async (req, res) => { const posts = await getPosts() - const newPostsList = posts.map((post) => { - const author = post.user.profile - ? { - firstName: post.user.profile.firstName, - lastName: post.user.profile.lastName - } - : { firstName: 'unknown', lastName: 'unknown' } - - return { - id: post.id, - content: post.content, - createdAt: post.createdAt, - updatedAt: post.updatedAt, - userId: post.user.id, - author - } - }) - return sendDataResponse(res, 200, { posts: newPostsList }) + return sendDataResponse(res, 200, { posts }) } diff --git a/src/domain/post.js b/src/domain/post.js index 7bed8167..42d4387b 100644 --- a/src/domain/post.js +++ b/src/domain/post.js @@ -18,7 +18,7 @@ export async function createPost(content, userId) { } export async function getPosts() { - return await dbClient.post.findMany({ + const posts = await dbClient.post.findMany({ include: { user: { include: { @@ -27,4 +27,24 @@ export async function getPosts() { } } }) + + const newPostsList = posts.map((post) => { + const author = post.user.profile + ? { + firstName: post.user.profile.firstName, + lastName: post.user.profile.lastName + } + : { firstName: 'unknown', lastName: 'unknown' } + + return { + id: post.id, + content: post.content, + createdAt: post.createdAt, + updatedAt: post.updatedAt, + userId: post.user.id, + author + } + }) + + return newPostsList } From 3a3680f01eb44f85feb4f2efa6031ba99c27e485 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Tue, 6 Feb 2024 17:47:59 +0000 Subject: [PATCH 28/86] feat: update getPosts to throw an error if user.profile is missing --- src/domain/post.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/domain/post.js b/src/domain/post.js index 42d4387b..064e9e3f 100644 --- a/src/domain/post.js +++ b/src/domain/post.js @@ -30,11 +30,12 @@ export async function getPosts() { const newPostsList = posts.map((post) => { const author = post.user.profile - ? { - firstName: post.user.profile.firstName, - lastName: post.user.profile.lastName - } - : { firstName: 'unknown', lastName: 'unknown' } + + if (!author || !author.firstName || !author.lastName) { + throw new Error( + `missing profile property on post.user at post with id:${post.id}` + ) + } return { id: post.id, From 9d9756bcbd2bb5ca88a47bf178527d3678e42b68 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Tue, 6 Feb 2024 18:09:22 +0000 Subject: [PATCH 29/86] fix: post.author only has two properties --- src/domain/post.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/domain/post.js b/src/domain/post.js index 064e9e3f..bbff57b7 100644 --- a/src/domain/post.js +++ b/src/domain/post.js @@ -29,14 +29,19 @@ export async function getPosts() { }) const newPostsList = posts.map((post) => { - const author = post.user.profile + const profile = post.user.profile - if (!author || !author.firstName || !author.lastName) { + if (!profile || !profile.firstName || !profile.lastName) { throw new Error( `missing profile property on post.user at post with id:${post.id}` ) } + const author = { + firstName: profile.firstName, + lastName: profile.lastName + } + return { id: post.id, content: post.content, From c7038415c58b1c607ae145607afa616b17a10053 Mon Sep 17 00:00:00 2001 From: Faiza Date: Wed, 7 Feb 2024 11:40:56 +0000 Subject: [PATCH 30/86] findManyByFirstOrLastName --- src/domain/user.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/domain/user.js b/src/domain/user.js index fd7734c7..e399f241 100644 --- a/src/domain/user.js +++ b/src/domain/user.js @@ -130,6 +130,19 @@ export default class User { return User._findMany('firstName', firstName) } + static async findManyByFirstNameOrLastName(name) { + return await dbClient.user.findMany({ + where: { + profile: { + OR: [{ firstName: name }, { lastName: name }] + } + }, + include: { + profile: true + } + }) + } + static async findAll() { return User._findMany() } From 4670ccdda971abfd0d6b6fed2a02d777ab887864 Mon Sep 17 00:00:00 2001 From: Satoki Date: Wed, 7 Feb 2024 12:42:10 +0000 Subject: [PATCH 31/86] amended status code to 409 for exising user when registering --- prisma/seed.js | 125 ++++++++++++++++++++++++---------------- src/controllers/user.js | 3 +- 2 files changed, 77 insertions(+), 51 deletions(-) diff --git a/prisma/seed.js b/prisma/seed.js index e6c288f2..21684795 100644 --- a/prisma/seed.js +++ b/prisma/seed.js @@ -1,74 +1,99 @@ import { PrismaClient } from '@prisma/client' import bcrypt from 'bcrypt' -const prisma = new PrismaClient(); +const prisma = new PrismaClient() async function seed() { - const cohort = await createCohort() + const cohort = await createCohort() - const student = await createUser('student@test.com', 'Testpassword1!', cohort.id, 'Joe', 'Bloggs', 'Hello, world!', 'student1') - const teacher = await createUser('teacher@test.com', 'Testpassword1!', null, 'Rick', 'Sanchez', 'Hello there!', 'teacher1', 'TEACHER') + const student = await createUser( + 'student@test.com', + 'Testpassword1!', + cohort.id, + 'Joe', + 'Bloggs', + 'Hello, world!', + 'student1' + ) + const teacher = await createUser( + 'teacher@test.com', + 'Testpassword1!', + null, + 'Rick', + 'Sanchez', + 'Hello there!', + 'teacher1', + 'TEACHER' + ) - await createPost(student.id, 'My first post!') - await createPost(teacher.id, 'Hello, students') + await createPost(student.id, 'My first post!') + await createPost(teacher.id, 'Hello, students') - process.exit(0); + process.exit(0) } async function createPost(userId, content) { - const post = await prisma.post.create({ - data: { - userId, - content - }, - include: { - user: true - } - }) + const post = await prisma.post.create({ + data: { + userId, + content + }, + include: { + user: true + } + }) - console.info('Post created', post) + console.info('Post created', post) - return post + return post } async function createCohort() { - const cohort = await prisma.cohort.create({ - data: {} - }) + const cohort = await prisma.cohort.create({ + data: {} + }) - console.info('Cohort created', cohort) + console.info('Cohort created', cohort) - return cohort + return cohort } -async function createUser(email, password, cohortId, firstName, lastName, bio, githubUrl, role = 'STUDENT') { - const user = await prisma.user.create({ - data: { - email, - password: await bcrypt.hash(password, 8), - role, - cohortId, - profile: { - create: { - firstName, - lastName, - bio, - githubUrl - } - } - }, - include: { - profile: true +async function createUser( + email, + password, + cohortId, + firstName, + lastName, + bio, + githubUrl, + role = 'STUDENT' +) { + const user = await prisma.user.create({ + data: { + email, + password: await bcrypt.hash(password, 8), + role, + cohortId, + profile: { + create: { + firstName, + lastName, + bio, + githubUrl } - }) + } + }, + include: { + profile: true + } + }) - console.info(`${role} created`, user) + console.info(`${role} created`, user) - return user + return user } -seed() - .catch(async e => { - console.error(e); - await prisma.$disconnect(); - process.exit(1) - }) \ No newline at end of file +seed().catch(async (e) => { + console.error(e) + await prisma.$disconnect() + process.exit(1) +}) diff --git a/src/controllers/user.js b/src/controllers/user.js index 40ff0f1c..da4be548 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -8,10 +8,11 @@ export const create = async (req, res) => { const existingUser = await User.findByEmail(userToCreate.email) if (existingUser) { - return sendDataResponse(res, 400, { email: 'Email already in use' }) + return sendDataResponse(res, 409, { email: 'Email already in use' }) } const createdUser = await userToCreate.save() + console.log(createdUser) return sendDataResponse(res, 201, createdUser) } catch (error) { From ae073b333dffc8b9ff26796f7aa80421960e9a92 Mon Sep 17 00:00:00 2001 From: Satoki Date: Wed, 7 Feb 2024 12:51:13 +0000 Subject: [PATCH 32/86] removed redundant console log, added error printout --- src/controllers/user.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/user.js b/src/controllers/user.js index da4be548..5c3c31fa 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -12,10 +12,10 @@ export const create = async (req, res) => { } const createdUser = await userToCreate.save() - console.log(createdUser) return sendDataResponse(res, 201, createdUser) } catch (error) { + console.error('Error creating user', error) return sendMessageResponse(res, 500, 'Unable to create new user') } } From 2fa02f54a22a841fb6753583ea2bd103d82fd3c6 Mon Sep 17 00:00:00 2001 From: Eduard Bissell Date: Wed, 7 Feb 2024 14:51:27 +0000 Subject: [PATCH 33/86] feat: amend getAll() to take name query --- src/controllers/user.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/controllers/user.js b/src/controllers/user.js index 40ff0f1c..de8cfcb6 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -36,15 +36,17 @@ export const getById = async (req, res) => { } export const getAll = async (req, res) => { - // eslint-disable-next-line camelcase - const { first_name: firstName } = req.query + const { name } = req.query - let foundUsers + const nameParts = new Set(name.split(' ')) - if (firstName) { - foundUsers = await User.findManyByFirstName(firstName) + const foundUsers = [] + if (name) { + nameParts.forEach(async (word, index) => { + foundUsers.push(await User.findManyByFirstNameOrLastName(word)) + }) } else { - foundUsers = await User.findAll() + foundUsers.push(await User.findAll()) } const formattedUsers = foundUsers.map((user) => { From a8b4b605d4849c1e112697472be6fe3be962c718 Mon Sep 17 00:00:00 2001 From: Eduard Bissell Date: Wed, 7 Feb 2024 15:25:52 +0000 Subject: [PATCH 34/86] feat: amend getAll() to take name query --- src/controllers/user.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/controllers/user.js b/src/controllers/user.js index 40ff0f1c..de8cfcb6 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -36,15 +36,17 @@ export const getById = async (req, res) => { } export const getAll = async (req, res) => { - // eslint-disable-next-line camelcase - const { first_name: firstName } = req.query + const { name } = req.query - let foundUsers + const nameParts = new Set(name.split(' ')) - if (firstName) { - foundUsers = await User.findManyByFirstName(firstName) + const foundUsers = [] + if (name) { + nameParts.forEach(async (word, index) => { + foundUsers.push(await User.findManyByFirstNameOrLastName(word)) + }) } else { - foundUsers = await User.findAll() + foundUsers.push(await User.findAll()) } const formattedUsers = foundUsers.map((user) => { From 59ff539bd04808c5f7cc2d02161d63046334011c Mon Sep 17 00:00:00 2001 From: Eduard Bissell Date: Wed, 7 Feb 2024 15:28:17 +0000 Subject: [PATCH 35/86] build: install morgan express middleware --- package-lock.json | 119 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + src/server.js | 2 + 3 files changed, 122 insertions(+) diff --git a/package-lock.json b/package-lock.json index 5c51e1bd..52f48126 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "dotenv": "^16.0.0", "express": "^4.17.3", "jsonwebtoken": "^8.5.1", + "morgan": "^1.10.0", "swagger-ui-express": "^5.0.0", "yaml": "^2.3.4" }, @@ -431,6 +432,22 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/basic-auth/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, "node_modules/bcrypt": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.0.1.tgz", @@ -2386,6 +2403,45 @@ "node": ">=10" } }, + "node_modules/morgan": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", + "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", + "dependencies": { + "basic-auth": "~2.0.1", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-finished": "~2.3.0", + "on-headers": "~1.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/morgan/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/morgan/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/morgan/node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -2601,6 +2657,14 @@ "node": ">= 0.8" } }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -3887,6 +3951,21 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "requires": { + "safe-buffer": "5.1.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, "bcrypt": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.0.1.tgz", @@ -5349,6 +5428,41 @@ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" }, + "morgan": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", + "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", + "requires": { + "basic-auth": "~2.0.1", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-finished": "~2.3.0", + "on-headers": "~1.0.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "requires": { + "ee-first": "1.1.1" + } + } + } + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -5500,6 +5614,11 @@ "ee-first": "1.1.1" } }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", diff --git a/package.json b/package.json index f63f47f5..9702f36e 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "dotenv": "^16.0.0", "express": "^4.17.3", "jsonwebtoken": "^8.5.1", + "morgan": "^1.10.0", "swagger-ui-express": "^5.0.0", "yaml": "^2.3.4" } diff --git a/src/server.js b/src/server.js index a3f67eeb..e875f810 100644 --- a/src/server.js +++ b/src/server.js @@ -4,6 +4,7 @@ import YAML from 'yaml' import swaggerUi from 'swagger-ui-express' import express from 'express' import cors from 'cors' +import morgan from 'morgan' import userRouter from './routes/user.js' import postRouter from './routes/post.js' import authRouter from './routes/auth.js' @@ -13,6 +14,7 @@ import deliveryLogRouter from './routes/deliveryLog.js' const app = express() app.disable('x-powered-by') app.use(cors()) +app.use(morgan('dev')) app.use(express.json()) app.use(express.urlencoded({ extended: true })) From 40834b99410e241c25f90c9ae7efa4b20a0adebe Mon Sep 17 00:00:00 2001 From: Faiza Date: Wed, 7 Feb 2024 17:08:39 +0000 Subject: [PATCH 36/86] need to solve --- src/controllers/user.js | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/controllers/user.js b/src/controllers/user.js index de8cfcb6..ce81ebfc 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -37,17 +37,25 @@ export const getById = async (req, res) => { export const getAll = async (req, res) => { const { name } = req.query + console.log('name', name) - const nameParts = new Set(name.split(' ')) - - const foundUsers = [] + let foundUsers if (name) { - nameParts.forEach(async (word, index) => { - foundUsers.push(await User.findManyByFirstNameOrLastName(word)) - }) + const nameParts = Array.from(new Set(name.split(' '))) + + const results = Promise.all( + nameParts.map(async (word, index) => { + console.log('nameParts', nameParts) + return User.findManyByFirstNameOrLastName(word) + }) + ) + foundUsers = await results + foundUsers = foundUsers.flat() + console.log('founduser', foundUsers) } else { - foundUsers.push(await User.findAll()) + foundUsers = await User.findAll() } + console.log('foundUsers', foundUsers) const formattedUsers = foundUsers.map((user) => { return { From 83fa9683892fdf9844aa7becd01e50e757369c18 Mon Sep 17 00:00:00 2001 From: callumhayden Date: Thu, 8 Feb 2024 09:52:39 +0000 Subject: [PATCH 37/86] added new router --- .env.example | 5 --- prisma/schema.prisma | 90 ++++++++++++++++++++++---------------------- src/routes/post.js | 3 +- 3 files changed, 47 insertions(+), 51 deletions(-) delete mode 100644 .env.example diff --git a/.env.example b/.env.example deleted file mode 100644 index 932b9f1e..00000000 --- a/.env.example +++ /dev/null @@ -1,5 +0,0 @@ -PORT=4000 -DATABASE_URL="?schema=prisma" -SHADOW_DATABASE_URL="?schema=shadow" -JWT_SECRET="somesecurestring" -JWT_EXPIRY="24h" \ No newline at end of file diff --git a/prisma/schema.prisma b/prisma/schema.prisma index ae5083d0..e5d05546 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -17,65 +17,65 @@ enum Role { } model User { - id Int @id @default(autoincrement()) - email String @unique - password String - role Role @default(STUDENT) - profile Profile? - cohortId Int? - cohort Cohort? @relation(fields: [cohortId], references: [id]) - posts Post[] - deliveryLogs DeliveryLog[] - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + id Int @id @default(autoincrement()) + email String @unique + password String + role Role @default(STUDENT) + profile Profile? + cohortId Int? + cohort Cohort? @relation(fields: [cohortId], references: [id]) + posts Post[] + deliveryLogs DeliveryLog[] + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt } model Profile { - id Int @id @default(autoincrement()) - userId Int @unique - user User @relation(fields: [userId], references: [id]) - firstName String - lastName String - bio String? - githubUrl String? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - } + id Int @id @default(autoincrement()) + userId Int @unique + user User @relation(fields: [userId], references: [id]) + firstName String + lastName String + bio String? + githubUrl String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} model Cohort { - id Int @id @default(autoincrement()) - users User[] - deliveryLogs DeliveryLog[] - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - } + id Int @id @default(autoincrement()) + users User[] + deliveryLogs DeliveryLog[] + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} model Post { - id Int @id @default(autoincrement()) - content String - userId Int - user User @relation(fields: [userId], references: [id]) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - } + id Int @id @default(autoincrement()) + content String + userId Int + user User @relation(fields: [userId], references: [id]) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} model DeliveryLog { - id Int @id @default(autoincrement()) + id Int @id @default(autoincrement()) date DateTime userId Int - user User @relation(fields: [userId], references: [id]) + user User @relation(fields: [userId], references: [id]) cohortId Int - cohort Cohort @relation(fields: [cohortId], references: [id]) + cohort Cohort @relation(fields: [cohortId], references: [id]) lines DeliveryLogLine[] - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - } + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} model DeliveryLogLine { - id Int @id @default(autoincrement()) + id Int @id @default(autoincrement()) content String logId Int - log DeliveryLog @relation(fields: [logId], references: [id]) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - } + log DeliveryLog @relation(fields: [logId], references: [id]) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} diff --git a/src/routes/post.js b/src/routes/post.js index a7fbbfb3..89aff69b 100644 --- a/src/routes/post.js +++ b/src/routes/post.js @@ -1,10 +1,11 @@ import { Router } from 'express' -import { create, getAll } from '../controllers/post.js' +import { create, getAll, deletePost } from '../controllers/post.js' import { validateAuthentication } from '../middleware/auth.js' const router = Router() router.post('/', validateAuthentication, create) router.get('/', validateAuthentication, getAll) +router.delete('/:postid', validateAuthentication, deletePost) export default router From 0fe1b8bb8fe974603d02920815b0901499898b1b Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Thu, 8 Feb 2024 10:02:49 +0000 Subject: [PATCH 38/86] Add Comment model and likes for post --- .env.example | 5 - .../migration.sql | 14 ++ prisma/schema.prisma | 126 ++++++++++-------- 3 files changed, 83 insertions(+), 62 deletions(-) delete mode 100644 .env.example rename prisma/migrations/{20240206124649_add_updated_at_and_created_at => 20240208100112_add_new_model_comment}/migration.sql (79%) diff --git a/.env.example b/.env.example deleted file mode 100644 index 932b9f1e..00000000 --- a/.env.example +++ /dev/null @@ -1,5 +0,0 @@ -PORT=4000 -DATABASE_URL="?schema=prisma" -SHADOW_DATABASE_URL="?schema=shadow" -JWT_SECRET="somesecurestring" -JWT_EXPIRY="24h" \ No newline at end of file diff --git a/prisma/migrations/20240206124649_add_updated_at_and_created_at/migration.sql b/prisma/migrations/20240208100112_add_new_model_comment/migration.sql similarity index 79% rename from prisma/migrations/20240206124649_add_updated_at_and_created_at/migration.sql rename to prisma/migrations/20240208100112_add_new_model_comment/migration.sql index 01c8d682..a96e5151 100644 --- a/prisma/migrations/20240206124649_add_updated_at_and_created_at/migration.sql +++ b/prisma/migrations/20240208100112_add_new_model_comment/migration.sql @@ -23,6 +23,7 @@ ADD COLUMN "updatedAt" TIMESTAMP(3) NOT NULL; -- AlterTable ALTER TABLE "Post" ADD COLUMN "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, +ADD COLUMN "likes" INTEGER NOT NULL DEFAULT 0, ADD COLUMN "updatedAt" TIMESTAMP(3) NOT NULL; -- AlterTable @@ -32,3 +33,16 @@ ADD COLUMN "updatedAt" TIMESTAMP(3) NOT NULL; -- AlterTable ALTER TABLE "User" ADD COLUMN "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, ADD COLUMN "updatedAt" TIMESTAMP(3) NOT NULL; + +-- CreateTable +CREATE TABLE "Comment" ( + "id" SERIAL NOT NULL, + "postId" INTEGER NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Comment_pkey" PRIMARY KEY ("id") +); + +-- AddForeignKey +ALTER TABLE "Comment" ADD CONSTRAINT "Comment_postId_fkey" FOREIGN KEY ("postId") REFERENCES "Post"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index ae5083d0..aa21308f 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -2,80 +2,92 @@ // learn more about it in the docs: https://pris.ly/d/prisma-schema generator client { - provider = "prisma-client-js" + provider = "prisma-client-js" } datasource db { - provider = "postgresql" - url = env("DATABASE_URL") - shadowDatabaseUrl = env("SHADOW_DATABASE_URL") + provider = "postgresql" + url = env("DATABASE_URL") + shadowDatabaseUrl = env("SHADOW_DATABASE_URL") } enum Role { - STUDENT - TEACHER + STUDENT + TEACHER } model User { - id Int @id @default(autoincrement()) - email String @unique - password String - role Role @default(STUDENT) - profile Profile? - cohortId Int? - cohort Cohort? @relation(fields: [cohortId], references: [id]) - posts Post[] - deliveryLogs DeliveryLog[] - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + id Int @id @default(autoincrement()) + email String @unique + password String + role Role @default(STUDENT) + profile Profile? + cohortId Int? + cohort Cohort? @relation(fields: [cohortId], references: [id]) + posts Post[] + deliveryLogs DeliveryLog[] + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt } model Profile { - id Int @id @default(autoincrement()) - userId Int @unique - user User @relation(fields: [userId], references: [id]) - firstName String - lastName String - bio String? - githubUrl String? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - } + id Int @id @default(autoincrement()) + userId Int @unique + user User @relation(fields: [userId], references: [id]) + firstName String + lastName String + bio String? + githubUrl String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} model Cohort { - id Int @id @default(autoincrement()) - users User[] - deliveryLogs DeliveryLog[] - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - } + id Int @id @default(autoincrement()) + users User[] + deliveryLogs DeliveryLog[] + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} model Post { - id Int @id @default(autoincrement()) - content String - userId Int - user User @relation(fields: [userId], references: [id]) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - } + id Int @id @default(autoincrement()) + likes Int @default(0) + content String + userId Int + user User @relation(fields: [userId], references: [id]) + comments Comment[] + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model Comment { + id Int @id @default(autoincrement()) + + Post Post @relation(fields: [postId], references: [id]) + postId Int + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} model DeliveryLog { - id Int @id @default(autoincrement()) - date DateTime - userId Int - user User @relation(fields: [userId], references: [id]) - cohortId Int - cohort Cohort @relation(fields: [cohortId], references: [id]) - lines DeliveryLogLine[] - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - } + id Int @id @default(autoincrement()) + date DateTime + userId Int + user User @relation(fields: [userId], references: [id]) + cohortId Int + cohort Cohort @relation(fields: [cohortId], references: [id]) + lines DeliveryLogLine[] + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} model DeliveryLogLine { - id Int @id @default(autoincrement()) - content String - logId Int - log DeliveryLog @relation(fields: [logId], references: [id]) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - } + id Int @id @default(autoincrement()) + content String + logId Int + log DeliveryLog @relation(fields: [logId], references: [id]) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} From 5384e419c3ca02172ee83188dc4a14443ba8b16c Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Thu, 8 Feb 2024 10:13:44 +0000 Subject: [PATCH 39/86] Add fields to the comment and relation with user --- .../migration.sql | 13 +++++++++++++ prisma/schema.prisma | 5 +++++ 2 files changed, 18 insertions(+) create mode 100644 prisma/migrations/20240208101235_add_fields_to_the_comment_model/migration.sql diff --git a/prisma/migrations/20240208101235_add_fields_to_the_comment_model/migration.sql b/prisma/migrations/20240208101235_add_fields_to_the_comment_model/migration.sql new file mode 100644 index 00000000..f181eada --- /dev/null +++ b/prisma/migrations/20240208101235_add_fields_to_the_comment_model/migration.sql @@ -0,0 +1,13 @@ +/* + Warnings: + + - Added the required column `content` to the `Comment` table without a default value. This is not possible if the table is not empty. + - Added the required column `userId` to the `Comment` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "Comment" ADD COLUMN "content" TEXT NOT NULL, +ADD COLUMN "userId" INTEGER NOT NULL; + +-- AddForeignKey +ALTER TABLE "Comment" ADD CONSTRAINT "Comment_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index aa21308f..d67e7478 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -28,6 +28,7 @@ model User { deliveryLogs DeliveryLog[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt + Comment Comment[] } model Profile { @@ -64,8 +65,12 @@ model Post { model Comment { id Int @id @default(autoincrement()) + content String + Post Post @relation(fields: [postId], references: [id]) postId Int + user User @relation(fields: [userId], references: [id]) + userId Int createdAt DateTime @default(now()) updatedAt DateTime @updatedAt From 6cabc5fe55a4995b217efa8947ba09012119f0d7 Mon Sep 17 00:00:00 2001 From: callumhayden Date: Thu, 8 Feb 2024 10:13:48 +0000 Subject: [PATCH 40/86] deleting post has been added to the controller and domain file --- src/controllers/post.js | 37 ++++++++++++++++++++++++++++++++++++- src/domain/post.js | 26 ++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/src/controllers/post.js b/src/controllers/post.js index b29f27c4..94460f66 100644 --- a/src/controllers/post.js +++ b/src/controllers/post.js @@ -1,4 +1,8 @@ -import { createPost, getPosts } from '../domain/post.js' +import { + createPost, + getPosts, + deletePostByIdAndUserId +} from '../domain/post.js' import { sendDataResponse } from '../utils/responses.js' export const create = async (req, res) => { @@ -22,3 +26,34 @@ export const getAll = async (req, res) => { const posts = await getPosts() return sendDataResponse(res, 200, { posts }) } + +export const deletePost = async (req, res) => { + const postId = parseInt(req.params.postid) + const userId = req.user.id + + if (!postId) { + console.error('Invalid post id') + return sendDataResponse(res, 400, { error: 'Invalid post id' }) + } + + try { + const { error, status, success } = await deletePostByIdAndUserId( + postId, + userId + ) + + if (error) { + console.error(error) + return sendDataResponse(res, status, { error }) + } + + if (success) { + return sendDataResponse(res, 201, { + message: 'Post deleted successfully' + }) + } + } catch (error) { + console.error('Error deleting post:', error.message) + return sendDataResponse(res, 500, { error: 'Something went wrong' }) + } +} diff --git a/src/domain/post.js b/src/domain/post.js index bbff57b7..73df02b5 100644 --- a/src/domain/post.js +++ b/src/domain/post.js @@ -54,3 +54,29 @@ export async function getPosts() { return newPostsList } + +async function deletePostByIdAndUserId(postId, userId) { + const post = await dbClient.post.findUnique({ + where: { + id: postId + } + }) + + if (!post) { + return { error: 'Post not found', status: 404 } + } + + if (post.userId !== userId) { + return { error: 'You are not authorized to delete this post', status: 403 } + } + + await dbClient.post.delete({ + where: { + id: postId + } + }) + + return { success: true } +} + +export { deletePostByIdAndUserId } From aa2830e69bb65cd1ffdbd0c421dee21636b53767 Mon Sep 17 00:00:00 2001 From: Eduard Bissell Date: Thu, 8 Feb 2024 10:18:42 +0000 Subject: [PATCH 41/86] fix: amend method to return User instance --- src/domain/user.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/domain/user.js b/src/domain/user.js index e399f241..599a2b81 100644 --- a/src/domain/user.js +++ b/src/domain/user.js @@ -131,7 +131,7 @@ export default class User { } static async findManyByFirstNameOrLastName(name) { - return await dbClient.user.findMany({ + const foundUsers = await dbClient.user.findMany({ where: { profile: { OR: [{ firstName: name }, { lastName: name }] @@ -141,6 +141,7 @@ export default class User { profile: true } }) + return foundUsers.map((user) => User.fromDb(user)) } static async findAll() { From 29e3f8565f88f453ab18bbdb24dcdadbcc1a177d Mon Sep 17 00:00:00 2001 From: Eduard Bissell Date: Thu, 8 Feb 2024 10:21:11 +0000 Subject: [PATCH 42/86] fix: amend method input to be case insensitive --- src/domain/user.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/domain/user.js b/src/domain/user.js index 599a2b81..ee27a04c 100644 --- a/src/domain/user.js +++ b/src/domain/user.js @@ -134,7 +134,10 @@ export default class User { const foundUsers = await dbClient.user.findMany({ where: { profile: { - OR: [{ firstName: name }, { lastName: name }] + OR: [ + { firstName: { mode: 'insensitive', contains: name } }, + { lastName: { mode: 'insensitive', contains: name } } + ] } }, include: { From eed1ae95efb14c37580360c1a951c0c7a9f28612 Mon Sep 17 00:00:00 2001 From: Eduard Bissell Date: Thu, 8 Feb 2024 10:44:40 +0000 Subject: [PATCH 43/86] refactor: remove namePart set --- src/controllers/user.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/controllers/user.js b/src/controllers/user.js index 0896264b..02ae4a05 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -38,25 +38,20 @@ export const getById = async (req, res) => { export const getAll = async (req, res) => { const { name } = req.query - console.log('name', name) let foundUsers if (name) { - const nameParts = Array.from(new Set(name.split(' '))) - + const nameParts = name.split(' ') const results = Promise.all( nameParts.map(async (word, index) => { - console.log('nameParts', nameParts) return User.findManyByFirstNameOrLastName(word) }) ) foundUsers = await results foundUsers = foundUsers.flat() - console.log('founduser', foundUsers) } else { foundUsers = await User.findAll() } - console.log('foundUsers', foundUsers) const formattedUsers = foundUsers.map((user) => { return { From 0120c1b61e83ce2630b556d8da37f40aac4f79d2 Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Thu, 8 Feb 2024 11:15:01 +0000 Subject: [PATCH 44/86] Add new model Like and relation with userId and postId --- .../migration.sql | 25 +++++++++++++++++++ prisma/schema.prisma | 20 ++++++++++++--- 2 files changed, 42 insertions(+), 3 deletions(-) create mode 100644 prisma/migrations/20240208111427_create_new_model_like/migration.sql diff --git a/prisma/migrations/20240208111427_create_new_model_like/migration.sql b/prisma/migrations/20240208111427_create_new_model_like/migration.sql new file mode 100644 index 00000000..2677d2b9 --- /dev/null +++ b/prisma/migrations/20240208111427_create_new_model_like/migration.sql @@ -0,0 +1,25 @@ +/* + Warnings: + + - You are about to drop the column `likes` on the `Post` table. All the data in the column will be lost. + +*/ +-- AlterTable +ALTER TABLE "Post" DROP COLUMN "likes"; + +-- CreateTable +CREATE TABLE "Like" ( + "id" SERIAL NOT NULL, + "postId" INTEGER NOT NULL, + "userId" INTEGER NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Like_pkey" PRIMARY KEY ("id") +); + +-- AddForeignKey +ALTER TABLE "Like" ADD CONSTRAINT "Like_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Like" ADD CONSTRAINT "Like_postId_fkey" FOREIGN KEY ("postId") REFERENCES "Post"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index d67e7478..437f2dca 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -28,7 +28,8 @@ model User { deliveryLogs DeliveryLog[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt - Comment Comment[] + comment Comment[] + likes Like[] } model Profile { @@ -53,21 +54,34 @@ model Cohort { model Post { id Int @id @default(autoincrement()) - likes Int @default(0) content String userId Int user User @relation(fields: [userId], references: [id]) comments Comment[] + likes Like[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } +model Like { + id Int @id @default(autoincrement()) + + post Post @relation(fields: [postId], references: [id]) + postId Int + + user User @relation(fields: [userId], references: [id]) + userId Int + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + model Comment { id Int @id @default(autoincrement()) content String - Post Post @relation(fields: [postId], references: [id]) + post Post @relation(fields: [postId], references: [id]) postId Int user User @relation(fields: [userId], references: [id]) userId Int From c42c1a560e9460da4d7c84e25b19ce491a3669e5 Mon Sep 17 00:00:00 2001 From: Eduard Bissell Date: Thu, 8 Feb 2024 12:01:21 +0000 Subject: [PATCH 45/86] feat: endpoint returns userList sorted by result count --- src/controllers/user.js | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/controllers/user.js b/src/controllers/user.js index 02ae4a05..8193225a 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -39,16 +39,30 @@ export const getById = async (req, res) => { export const getAll = async (req, res) => { const { name } = req.query - let foundUsers + let foundUsers = [] if (name) { const nameParts = name.split(' ') - const results = Promise.all( - nameParts.map(async (word, index) => { + const promise = Promise.all( + nameParts.map(async (word) => { return User.findManyByFirstNameOrLastName(word) }) ) - foundUsers = await results - foundUsers = foundUsers.flat() + + let results = await promise + results = results.flat() + + results.forEach((user) => { + const { id } = user + const match = foundUsers.some((entry) => entry.id === id) + if (!match) { + user.count = 1 + foundUsers.push(user) + } else { + const dupeResult = foundUsers.find((entry) => entry.id === id) + dupeResult.count++ + } + }) + foundUsers.sort((a, b) => b.count - a.count) } else { foundUsers = await User.findAll() } From b10e010a5449c29deafe6fa16868856fd78175e9 Mon Sep 17 00:00:00 2001 From: callumhayden Date: Thu, 8 Feb 2024 12:39:38 +0000 Subject: [PATCH 46/86] delete function working, just need to fix the refresh issue --- src/controllers/post.js | 3 ++- src/routes/post.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/controllers/post.js b/src/controllers/post.js index 94460f66..39377e2a 100644 --- a/src/controllers/post.js +++ b/src/controllers/post.js @@ -28,7 +28,8 @@ export const getAll = async (req, res) => { } export const deletePost = async (req, res) => { - const postId = parseInt(req.params.postid) + console.log(req.params.postId) + const postId = parseInt(req.params.postId) const userId = req.user.id if (!postId) { diff --git a/src/routes/post.js b/src/routes/post.js index 89aff69b..8c439271 100644 --- a/src/routes/post.js +++ b/src/routes/post.js @@ -6,6 +6,6 @@ const router = Router() router.post('/', validateAuthentication, create) router.get('/', validateAuthentication, getAll) -router.delete('/:postid', validateAuthentication, deletePost) +router.delete('/:postId', validateAuthentication, deletePost) export default router From 7ff0b4ef1769cccdd56fdbd083ca93d30ef5fbdd Mon Sep 17 00:00:00 2001 From: Eduard Bissell Date: Thu, 8 Feb 2024 13:07:10 +0000 Subject: [PATCH 47/86] refactor: extract or query --- src/domain/user.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/domain/user.js b/src/domain/user.js index ee27a04c..53e19fcd 100644 --- a/src/domain/user.js +++ b/src/domain/user.js @@ -187,4 +187,23 @@ export default class User { return foundUsers.map((user) => User.fromDb(user)) } + + static async _findManyOr(value, ...keys) { + const query = keys.map((key) => ({ + [key]: { mode: 'insensitive', contains: value } + })) + + const foundUsers = await dbClient.user.findMany({ + where: { + profile: { + OR: query + } + }, + include: { + profile: true + } + }) + + return foundUsers.map((user) => User.fromDb(user)) + } } From e9d9206eadfa42349f765cf179066d889ad54061 Mon Sep 17 00:00:00 2001 From: Eduard Bissell Date: Thu, 8 Feb 2024 13:08:33 +0000 Subject: [PATCH 48/86] refactor: relocate controller logic to domain --- src/domain/user.js | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/src/domain/user.js b/src/domain/user.js index 53e19fcd..9c05b770 100644 --- a/src/domain/user.js +++ b/src/domain/user.js @@ -131,20 +131,31 @@ export default class User { } static async findManyByFirstNameOrLastName(name) { - const foundUsers = await dbClient.user.findMany({ - where: { - profile: { - OR: [ - { firstName: { mode: 'insensitive', contains: name } }, - { lastName: { mode: 'insensitive', contains: name } } - ] - } - }, - include: { - profile: true + const splitName = name.split(' ') + + const promise = Promise.all( + splitName.map((word) => { + return User._findManyOr(word, 'firstName', 'lastName') + }) + ) + + let results = await promise + results = results.flat() + + const foundUsers = [] + results.forEach((user) => { + const { id } = user + const match = foundUsers.some((entry) => entry.id === id) + if (!match) { + user.count = 1 + foundUsers.push(user) + } else { + const dupeResult = foundUsers.find((entry) => entry.id === id) + dupeResult.count++ } }) - return foundUsers.map((user) => User.fromDb(user)) + + return foundUsers.sort((a, b) => b.count - a.count) } static async findAll() { From c2cadae906768fda991cbfee6f91a28d98ee23b1 Mon Sep 17 00:00:00 2001 From: Eduard Bissell Date: Thu, 8 Feb 2024 13:18:03 +0000 Subject: [PATCH 49/86] refactor: move controller logic to domain --- src/controllers/user.js | 28 +--------------------------- 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/src/controllers/user.js b/src/controllers/user.js index 8193225a..0d6ca649 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -39,33 +39,7 @@ export const getById = async (req, res) => { export const getAll = async (req, res) => { const { name } = req.query - let foundUsers = [] - if (name) { - const nameParts = name.split(' ') - const promise = Promise.all( - nameParts.map(async (word) => { - return User.findManyByFirstNameOrLastName(word) - }) - ) - - let results = await promise - results = results.flat() - - results.forEach((user) => { - const { id } = user - const match = foundUsers.some((entry) => entry.id === id) - if (!match) { - user.count = 1 - foundUsers.push(user) - } else { - const dupeResult = foundUsers.find((entry) => entry.id === id) - dupeResult.count++ - } - }) - foundUsers.sort((a, b) => b.count - a.count) - } else { - foundUsers = await User.findAll() - } + const foundUsers = await User.findManyByFirstNameOrLastName(name) const formattedUsers = foundUsers.map((user) => { return { From 120dba32e4c8d714e4b704d2535722b64b4f20e5 Mon Sep 17 00:00:00 2001 From: callumhayden Date: Thu, 8 Feb 2024 13:22:36 +0000 Subject: [PATCH 50/86] created the edit post button, just need to work on client side now to finish it --- src/controllers/post.js | 29 ++++++++++++++++++++++++++++- src/domain/post.js | 21 +++++++++++++++++++++ src/routes/post.js | 3 ++- 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/src/controllers/post.js b/src/controllers/post.js index 39377e2a..14e834c0 100644 --- a/src/controllers/post.js +++ b/src/controllers/post.js @@ -1,7 +1,8 @@ import { createPost, getPosts, - deletePostByIdAndUserId + deletePostByIdAndUserId, + updatePostByIdAndUserId } from '../domain/post.js' import { sendDataResponse } from '../utils/responses.js' @@ -58,3 +59,29 @@ export const deletePost = async (req, res) => { return sendDataResponse(res, 500, { error: 'Something went wrong' }) } } + +export const editPost = async (req, res) => { + const postId = parseInt(req.params.postId) + const { content } = req.body // Assuming you want to update post content + const userId = req.user.id + + if (!postId) { + console.error('Invalid post id') + return sendDataResponse(res, 400, { error: 'Invalid post id' }) + } + + try { + const result = await updatePostByIdAndUserId(postId, userId, content) + if (result.error) { + return sendDataResponse(res, result.status, { error: result.error }) + } + + return sendDataResponse(res, 200, { + message: 'Post updated successfully', + post: result.post + }) + } catch (error) { + console.error('Error updating post:', error.message) + return sendDataResponse(res, 500, { error: 'Something went wrong' }) + } +} diff --git a/src/domain/post.js b/src/domain/post.js index 73df02b5..0aca7897 100644 --- a/src/domain/post.js +++ b/src/domain/post.js @@ -79,4 +79,25 @@ async function deletePostByIdAndUserId(postId, userId) { return { success: true } } +export async function updatePostByIdAndUserId(postId, userId, content) { + const post = await dbClient.post.findUnique({ + where: { id: postId } + }) + + if (!post) { + return { error: 'Post not found', status: 404 } + } + + if (post.userId !== userId) { + return { error: 'You are not authorized to update this post', status: 403 } + } + + const updatedPost = await dbClient.post.update({ + where: { id: postId }, + data: { content } + }) + + return { post: updatedPost } +} + export { deletePostByIdAndUserId } diff --git a/src/routes/post.js b/src/routes/post.js index 8c439271..3d409ddf 100644 --- a/src/routes/post.js +++ b/src/routes/post.js @@ -1,11 +1,12 @@ import { Router } from 'express' -import { create, getAll, deletePost } from '../controllers/post.js' +import { create, getAll, deletePost, editPost } from '../controllers/post.js' import { validateAuthentication } from '../middleware/auth.js' const router = Router() router.post('/', validateAuthentication, create) router.get('/', validateAuthentication, getAll) +router.put('/:postId', validateAuthentication, editPost) router.delete('/:postId', validateAuthentication, deletePost) export default router From cc2ae8028dd1722a87685ab8dc4d30c418f53293 Mon Sep 17 00:00:00 2001 From: Eduard Bissell Date: Thu, 8 Feb 2024 13:28:15 +0000 Subject: [PATCH 51/86] docs: update docs --- docs/openapi.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/openapi.yml b/docs/openapi.yml index 5f2a05f2..742d4c3a 100644 --- a/docs/openapi.yml +++ b/docs/openapi.yml @@ -35,15 +35,15 @@ paths: get: tags: - user - summary: Get all users by first name if provided + summary: Get all users by name if provided description: '' operationId: getAllUsers security: - bearerAuth: [] parameters: - - name: firstName + - name: name in: query - description: Search all users by first name if provided (case-sensitive and exact string matches only) + description: Search all users by name if provided. Name is case insensitive and will return matches for both first name and last name. schema: type: string responses: From 48e8ebd41d252327c0e0b86e6525ab55eb29e9d1 Mon Sep 17 00:00:00 2001 From: callumhayden Date: Thu, 8 Feb 2024 14:07:13 +0000 Subject: [PATCH 52/86] edit and delete server side is fully working --- src/controllers/post.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/post.js b/src/controllers/post.js index 14e834c0..800aa35c 100644 --- a/src/controllers/post.js +++ b/src/controllers/post.js @@ -62,7 +62,7 @@ export const deletePost = async (req, res) => { export const editPost = async (req, res) => { const postId = parseInt(req.params.postId) - const { content } = req.body // Assuming you want to update post content + const { content } = req.body const userId = req.user.id if (!postId) { From 1b0e86d37a56aeeb7d60dbb74fa30b765df289f6 Mon Sep 17 00:00:00 2001 From: callumhayden Date: Thu, 8 Feb 2024 14:57:43 +0000 Subject: [PATCH 53/86] fully refactored code to meet review requirements --- src/controllers/post.js | 32 ++++++++++---------------------- src/domain/post.js | 2 -- 2 files changed, 10 insertions(+), 24 deletions(-) diff --git a/src/controllers/post.js b/src/controllers/post.js index 800aa35c..7757285f 100644 --- a/src/controllers/post.js +++ b/src/controllers/post.js @@ -29,33 +29,20 @@ export const getAll = async (req, res) => { } export const deletePost = async (req, res) => { - console.log(req.params.postId) const postId = parseInt(req.params.postId) const userId = req.user.id - if (!postId) { - console.error('Invalid post id') - return sendDataResponse(res, 400, { error: 'Invalid post id' }) - } - try { - const { error, status, success } = await deletePostByIdAndUserId( - postId, - userId - ) - - if (error) { - console.error(error) - return sendDataResponse(res, status, { error }) - } - - if (success) { - return sendDataResponse(res, 201, { + const result = await deletePostByIdAndUserId(postId, userId) + if (result && result.error) { + return sendDataResponse(res, result.status, { error: result.error }) + } else { + return sendDataResponse(res, 200, { message: 'Post deleted successfully' }) } } catch (error) { - console.error('Error deleting post:', error.message) + console.error('Error deleting post:', error) return sendDataResponse(res, 500, { error: 'Something went wrong' }) } } @@ -66,13 +53,14 @@ export const editPost = async (req, res) => { const userId = req.user.id if (!postId) { - console.error('Invalid post id') - return sendDataResponse(res, 400, { error: 'Invalid post id' }) + console.error('Post ID does not exist') + return sendDataResponse(res, 400, { error: 'Post ID does not exist' }) } try { const result = await updatePostByIdAndUserId(postId, userId, content) if (result.error) { + console.error('Error updating post:', result.error) // Log the error here as well return sendDataResponse(res, result.status, { error: result.error }) } @@ -81,7 +69,7 @@ export const editPost = async (req, res) => { post: result.post }) } catch (error) { - console.error('Error updating post:', error.message) + console.error('Exception error updating post:', error.message) // This captures exceptions thrown during the process return sendDataResponse(res, 500, { error: 'Something went wrong' }) } } diff --git a/src/domain/post.js b/src/domain/post.js index 0aca7897..75f9e64e 100644 --- a/src/domain/post.js +++ b/src/domain/post.js @@ -75,8 +75,6 @@ async function deletePostByIdAndUserId(postId, userId) { id: postId } }) - - return { success: true } } export async function updatePostByIdAndUserId(postId, userId, content) { From 200f74c33ed2ec8b033e7129c7b30f3d35ca3846 Mon Sep 17 00:00:00 2001 From: Eduard Bissell Date: Thu, 8 Feb 2024 15:25:57 +0000 Subject: [PATCH 54/86] fix: add guard clause --- src/domain/user.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/domain/user.js b/src/domain/user.js index 9c05b770..e561a53d 100644 --- a/src/domain/user.js +++ b/src/domain/user.js @@ -200,6 +200,17 @@ export default class User { } static async _findManyOr(value, ...keys) { + const validValue = typeof value === 'string' || typeof value === 'number' + const validKeys = keys.every( + (key) => typeof key === 'string' && key.length > 0 + ) + + if (!validValue || !validKeys) { + throw new Error( + "Invalid method inputs. Value must be of type 'string' or 'number'. Keys must be of type 'string'." + ) + } + const query = keys.map((key) => ({ [key]: { mode: 'insensitive', contains: value } })) From f616fb41f1d53fbf229059ee9328e7ec95234dab Mon Sep 17 00:00:00 2001 From: Eduard Bissell Date: Thu, 8 Feb 2024 15:45:02 +0000 Subject: [PATCH 55/86] refactor: relocate query options --- src/domain/user.js | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/domain/user.js b/src/domain/user.js index e561a53d..bffa5f77 100644 --- a/src/domain/user.js +++ b/src/domain/user.js @@ -135,7 +135,13 @@ export default class User { const promise = Promise.all( splitName.map((word) => { - return User._findManyOr(word, 'firstName', 'lastName') + return User._findManyOr( + { + key: 'firstName', + value: { mode: 'insensitive', contains: word } + }, + { key: 'lastName', value: { mode: 'insensitive', contains: word } } + ) }) ) @@ -199,20 +205,9 @@ export default class User { return foundUsers.map((user) => User.fromDb(user)) } - static async _findManyOr(value, ...keys) { - const validValue = typeof value === 'string' || typeof value === 'number' - const validKeys = keys.every( - (key) => typeof key === 'string' && key.length > 0 - ) - - if (!validValue || !validKeys) { - throw new Error( - "Invalid method inputs. Value must be of type 'string' or 'number'. Keys must be of type 'string'." - ) - } - - const query = keys.map((key) => ({ - [key]: { mode: 'insensitive', contains: value } + static async _findManyOr(...keyValue) { + const query = keyValue.map(({ key, value }) => ({ + [key]: value })) const foundUsers = await dbClient.user.findMany({ From 190d5cfe358a269d174cbd97dcca469d3b60d907 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Thu, 8 Feb 2024 15:51:40 +0000 Subject: [PATCH 56/86] feat: GET/posts route: posts include comments and likes --- src/domain/post.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/domain/post.js b/src/domain/post.js index bbff57b7..cae69364 100644 --- a/src/domain/post.js +++ b/src/domain/post.js @@ -24,7 +24,9 @@ export async function getPosts() { include: { profile: true } - } + }, + comments: true, + likes: true } }) @@ -48,6 +50,8 @@ export async function getPosts() { createdAt: post.createdAt, updatedAt: post.updatedAt, userId: post.user.id, + comments: post.comments, + likes: post.likes, author } }) From a6a818b40b4c50ace914a579ab7d7ec4d43c334b Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Thu, 8 Feb 2024 16:00:51 +0000 Subject: [PATCH 57/86] build: add comments and likes to posts in seed --- prisma/seed.js | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/prisma/seed.js b/prisma/seed.js index 21684795..70df48ff 100644 --- a/prisma/seed.js +++ b/prisma/seed.js @@ -25,20 +25,36 @@ async function seed() { 'TEACHER' ) - await createPost(student.id, 'My first post!') - await createPost(teacher.id, 'Hello, students') + await createPost( + student.id, + 'My first post!', + [ + { content: 'hi', userId: 2 }, + { content: "'sup?", userId: 2 } + ], + [{ userId: 2 }] + ) + await createPost(teacher.id, 'Hello, students', [], [{ userId: 1 }]) process.exit(0) } -async function createPost(userId, content) { +async function createPost(userId, content, comments, likes) { const post = await prisma.post.create({ data: { userId, - content + content, + comments: { + create: comments + }, + likes: { + create: likes + } }, include: { - user: true + user: true, + comments: true, + likes: true } }) From aa89ce256369c4f0e60e9106137efc2fbfce0f2d Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Thu, 8 Feb 2024 16:10:34 +0000 Subject: [PATCH 58/86] docs: update .yml to reflect changes --- docs/openapi.yml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/docs/openapi.yml b/docs/openapi.yml index 5f2a05f2..7828c524 100644 --- a/docs/openapi.yml +++ b/docs/openapi.yml @@ -411,6 +411,37 @@ components: updatedAt: type: string format: string + comments: + type: array + items: + type: object + properties: + id: + type: integer + postId: + type: integer + userId: + type: integer + content: + type: string + format: string + createdAt: + type: string + format: string + updatedAt: + type: string + format: string + likes: + type: array + items: + type: object + properties: + id: + type: integer + userId: + type: integer + postId: + type: integer author: type: object properties: From 08eec8ff711d25b55b74051815e37ebf262e6bab Mon Sep 17 00:00:00 2001 From: callumhayden Date: Thu, 8 Feb 2024 16:14:17 +0000 Subject: [PATCH 59/86] more chanages to the requirements made --- src/controllers/post.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/controllers/post.js b/src/controllers/post.js index 7757285f..58e0c637 100644 --- a/src/controllers/post.js +++ b/src/controllers/post.js @@ -29,7 +29,7 @@ export const getAll = async (req, res) => { } export const deletePost = async (req, res) => { - const postId = parseInt(req.params.postId) + const postId = Number(req.params.postId) const userId = req.user.id try { @@ -48,13 +48,13 @@ export const deletePost = async (req, res) => { } export const editPost = async (req, res) => { - const postId = parseInt(req.params.postId) + const postId = Number(req.params.postId) const { content } = req.body const userId = req.user.id if (!postId) { - console.error('Post ID does not exist') - return sendDataResponse(res, 400, { error: 'Post ID does not exist' }) + console.error('postId is required') + return sendDataResponse(res, 400, { error: 'postId is required' }) } try { From 9310fb2fe6af543613db13fb29b2a01e6d89ef89 Mon Sep 17 00:00:00 2001 From: Eduard Bissell Date: Thu, 8 Feb 2024 16:41:06 +0000 Subject: [PATCH 60/86] fix: handle case where no query param in request --- src/controllers/user.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/controllers/user.js b/src/controllers/user.js index 0d6ca649..81cf3295 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -39,7 +39,9 @@ export const getById = async (req, res) => { export const getAll = async (req, res) => { const { name } = req.query - const foundUsers = await User.findManyByFirstNameOrLastName(name) + let foundUsers + if (name) foundUsers = await User.findManyByFirstNameOrLastName(name) + else foundUsers = await User.findAll() const formattedUsers = foundUsers.map((user) => { return { From 0c729db5e83f7d28f406a521668e9a34125ae674 Mon Sep 17 00:00:00 2001 From: Eduard Bissell Date: Thu, 8 Feb 2024 16:52:20 +0000 Subject: [PATCH 61/86] refactor: add curly brackets --- src/controllers/user.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/controllers/user.js b/src/controllers/user.js index 81cf3295..284adae4 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -40,8 +40,11 @@ export const getAll = async (req, res) => { const { name } = req.query let foundUsers - if (name) foundUsers = await User.findManyByFirstNameOrLastName(name) - else foundUsers = await User.findAll() + if (name) { + foundUsers = await User.findManyByFirstNameOrLastName(name) + } else { + foundUsers = await User.findAll() + } const formattedUsers = foundUsers.map((user) => { return { From e59c7639d42e42786a1b870ff2c680e56c4308fd Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Thu, 8 Feb 2024 17:10:12 +0000 Subject: [PATCH 62/86] docs: add missing properties to Posts schema --- docs/openapi.yml | 60 ++++++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/docs/openapi.yml b/docs/openapi.yml index 7828c524..f3b210f5 100644 --- a/docs/openapi.yml +++ b/docs/openapi.yml @@ -2,7 +2,7 @@ openapi: 3.0.3 info: title: Team Dev Server API description: |- - version: 1.0 + version: '1.0' servers: - url: http://localhost:4000/ @@ -204,13 +204,13 @@ paths: security: - bearerAuth: [] responses: - '200': + 200: description: Successful operation content: application/json: schema: $ref: '#/components/schemas/Posts' - '401': + 401: description: fail content: application/json: @@ -306,6 +306,10 @@ components: type: integer content: type: string + comments: + type: array + items: + type: object Cohort: type: object @@ -405,12 +409,26 @@ components: type: integer content: type: string - createdAt: - type: string - format: string - updatedAt: - type: string format: string + author: + type: object + properties: + id: + type: integer + cohortId: + type: integer + role: + type: string + firstName: + type: string + lastName: + type: string + bio: + type: string + githubUrl: + type: string + profileImageUrl: + type: string comments: type: array items: @@ -442,26 +460,12 @@ components: type: integer postId: type: integer - author: - type: object - properties: - id: - type: integer - cohortId: - type: integer - role: - type: string - firstName: - type: string - lastName: - type: string - bio: - type: string - githubUrl: - type: string - profileImageUrl: - type: string - + createdAt: + type: string + format: string + updatedAt: + type: string + CreatedUser: type: object properties: From 58bd99968fe9fb5559ba8c0dd7c3ebb4bf74d2f9 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Thu, 8 Feb 2024 17:20:01 +0000 Subject: [PATCH 63/86] fix: remove redundancies --- docs/openapi.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/openapi.yml b/docs/openapi.yml index f3b210f5..a50a121f 100644 --- a/docs/openapi.yml +++ b/docs/openapi.yml @@ -306,10 +306,6 @@ components: type: integer content: type: string - comments: - type: array - items: - type: object Cohort: type: object From 3b695775190bc246d16625bd27c5f624497688fb Mon Sep 17 00:00:00 2001 From: callumhayden Date: Thu, 8 Feb 2024 17:57:19 +0000 Subject: [PATCH 64/86] added the put to the docs, just need to add the delete one and place in the correct place --- docs/openapi.yml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/docs/openapi.yml b/docs/openapi.yml index 742d4c3a..a78c3f42 100644 --- a/docs/openapi.yml +++ b/docs/openapi.yml @@ -195,6 +195,37 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' + + put: + tags: + - put + summary: Update post + description: you can update your own post + operationId: editPost + security: + - bearerAuth: [] + requestBody: + description: Updated post object + content: + application/json: + schema: + type: object + properties: + content: + type: string + responses: + 200: + description: success + content: + application/json: + schema: + $ref: '#/components/schemas/Post' + 400: + description: fail + content: + application/json: + schema: + $ref: '#/components/schemas/Error' get: tags: - post From bb5b612e5b4a624ecd3500d64f3761fe829e74da Mon Sep 17 00:00:00 2001 From: callumhayden Date: Fri, 9 Feb 2024 09:59:58 +0000 Subject: [PATCH 65/86] updated the .yml files for the put and delete functions --- docs/openapi.yml | 43 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/docs/openapi.yml b/docs/openapi.yml index a78c3f42..52f60a8c 100644 --- a/docs/openapi.yml +++ b/docs/openapi.yml @@ -183,22 +183,21 @@ paths: content: type: string responses: - 201: + '201': description: success content: application/json: schema: $ref: '#/components/schemas/Post' - 400: + '400': description: fail content: application/json: schema: $ref: '#/components/schemas/Error' - put: tags: - - put + - post summary: Update post description: you can update your own post operationId: editPost @@ -214,18 +213,48 @@ paths: content: type: string responses: - 200: + '200': description: success content: application/json: schema: $ref: '#/components/schemas/Post' - 400: + '400': description: fail content: application/json: schema: - $ref: '#/components/schemas/Error' + $ref: '#/components/schemas/Error' + delete: + tags: + - post + summary: Delete post + description: you can delete your own post + operationId: deletePost + security: + - bearerAuth: [] + requestBody: + description: Updated post object + content: + application/json: + schema: + type: object + properties: + content: + type: string + responses: + '200': + description: success + content: + application/json: + schema: + $ref: '#/components/schemas/Post' + '500': + description: fail + content: + application/json: + schema: + $ref: '#/components/schemas/Error' get: tags: - post From cacf71a88e87cd2b312a2df263a29e00d8a68859 Mon Sep 17 00:00:00 2001 From: LAVINIABENZAR Date: Fri, 9 Feb 2024 02:23:52 -0800 Subject: [PATCH 66/86] Comment, Like and Post models created / updated --- prisma/schema.prisma | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 667f267c..6824ac6b 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -25,6 +25,8 @@ model User { cohortId Int? cohort Cohort? @relation(fields: [cohortId], references: [id]) posts Post[] + comments Comment[] + likes Like[] deliveryLogs DeliveryLog[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@ -55,6 +57,29 @@ model Post { content String userId Int user User @relation(fields: [userId], references: [id]) + comments Comment[] + likes Like[] + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model Like { + id Int @id @default(autoincrement()) + postId Int + post Post @relation(fields: [postId], references: [id]) + userId Int + user User @relation(fields: [userId], references: [id]) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model Comment { + id Int @id @default(autoincrement()) + content String + postId Int + post Post @relation(fields: [postId], references: [id]) + userId Int + user User @relation(fields: [userId], references: [id]) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } From 964f087ace7dd6cdd9c49e5ef8310cecceefac47 Mon Sep 17 00:00:00 2001 From: callumhayden Date: Fri, 9 Feb 2024 13:05:11 +0000 Subject: [PATCH 67/86] created a new router, controller and domain file for the like function --- src/controllers/post.js | 16 ++++++++++- src/domain/post.js | 61 ++++++++++++++++++++++------------------- src/routes/post.js | 9 +++++- 3 files changed, 56 insertions(+), 30 deletions(-) diff --git a/src/controllers/post.js b/src/controllers/post.js index 58e0c637..ba1503e0 100644 --- a/src/controllers/post.js +++ b/src/controllers/post.js @@ -2,7 +2,8 @@ import { createPost, getPosts, deletePostByIdAndUserId, - updatePostByIdAndUserId + updatePostByIdAndUserId, + toggleLike } from '../domain/post.js' import { sendDataResponse } from '../utils/responses.js' @@ -73,3 +74,16 @@ export const editPost = async (req, res) => { return sendDataResponse(res, 500, { error: 'Something went wrong' }) } } + +export const likePost = async (req, res) => { + const { postId } = req.params + const userId = req.user.id + + try { + const message = await toggleLike(Number(postId), userId) + res.status(200).json({ message }) + } catch (error) { + console.error('Error handling like action:', error) + res.status(500).json({ error: 'Internal server error' }) + } +} diff --git a/src/domain/post.js b/src/domain/post.js index 15eb1ba5..d00daffa 100644 --- a/src/domain/post.js +++ b/src/domain/post.js @@ -30,20 +30,14 @@ export async function getPosts() { } }) - const newPostsList = posts.map((post) => { - const profile = post.user.profile - + return posts.map((post) => { + const { profile } = post.user if (!profile || !profile.firstName || !profile.lastName) { throw new Error( - `missing profile property on post.user at post with id:${post.id}` + `Missing profile property on post.user at post with id: ${post.id}` ) } - const author = { - firstName: profile.firstName, - lastName: profile.lastName - } - return { id: post.id, content: post.content, @@ -51,20 +45,17 @@ export async function getPosts() { updatedAt: post.updatedAt, userId: post.user.id, comments: post.comments, - likes: post.likes, - author + likes: post.likes.length, + author: { + firstName: profile.firstName, + lastName: profile.lastName + } } }) - - return newPostsList } -async function deletePostByIdAndUserId(postId, userId) { - const post = await dbClient.post.findUnique({ - where: { - id: postId - } - }) +export async function deletePostByIdAndUserId(postId, userId) { + const post = await dbClient.post.findUnique({ where: { id: postId } }) if (!post) { return { error: 'Post not found', status: 404 } @@ -74,17 +65,12 @@ async function deletePostByIdAndUserId(postId, userId) { return { error: 'You are not authorized to delete this post', status: 403 } } - await dbClient.post.delete({ - where: { - id: postId - } - }) + await dbClient.post.delete({ where: { id: postId } }) + return { message: 'Post deleted successfully' } } export async function updatePostByIdAndUserId(postId, userId, content) { - const post = await dbClient.post.findUnique({ - where: { id: postId } - }) + const post = await dbClient.post.findUnique({ where: { id: postId } }) if (!post) { return { error: 'Post not found', status: 404 } @@ -102,4 +88,23 @@ export async function updatePostByIdAndUserId(postId, userId, content) { return { post: updatedPost } } -export { deletePostByIdAndUserId } +export async function toggleLike(postId, userId) { + const existingLike = await dbClient.like.findFirst({ + where: { + AND: [{ postId: postId }, { userId: userId }] + } + }) + + if (existingLike) { + await dbClient.like.delete({ where: { id: existingLike.id } }) + return 'Like removed successfully.' + } else { + await dbClient.like.create({ + data: { + postId, + userId + } + }) + return 'Like added successfully.' + } +} diff --git a/src/routes/post.js b/src/routes/post.js index 3d409ddf..0015dc40 100644 --- a/src/routes/post.js +++ b/src/routes/post.js @@ -1,10 +1,17 @@ import { Router } from 'express' -import { create, getAll, deletePost, editPost } from '../controllers/post.js' +import { + create, + getAll, + deletePost, + editPost, + likePost +} from '../controllers/post.js' import { validateAuthentication } from '../middleware/auth.js' const router = Router() router.post('/', validateAuthentication, create) +router.post('/:postId/like', validateAuthentication, likePost) router.get('/', validateAuthentication, getAll) router.put('/:postId', validateAuthentication, editPost) router.delete('/:postId', validateAuthentication, deletePost) From c1dfa5641f830d0d9e8858e9312bc7a387dc92c4 Mon Sep 17 00:00:00 2001 From: callumhayden Date: Fri, 9 Feb 2024 14:20:44 +0000 Subject: [PATCH 68/86] updated the code to meet review requirements --- src/domain/post.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/domain/post.js b/src/domain/post.js index d00daffa..f627e1b1 100644 --- a/src/domain/post.js +++ b/src/domain/post.js @@ -98,13 +98,13 @@ export async function toggleLike(postId, userId) { if (existingLike) { await dbClient.like.delete({ where: { id: existingLike.id } }) return 'Like removed successfully.' - } else { - await dbClient.like.create({ - data: { - postId, - userId - } - }) - return 'Like added successfully.' } + + await dbClient.like.create({ + data: { + postId, + userId + } + }) + return 'Like added successfully.' } From 7fc4771787970012534903b60ff14e30e6498a28 Mon Sep 17 00:00:00 2001 From: LAVINIABENZAR Date: Fri, 9 Feb 2024 09:51:54 -0800 Subject: [PATCH 69/86] get endpoint for comments --- src/controllers/comment.js | 7 +++++++ src/domain/comment.js | 33 +++++++++++++++++++++++++++++++++ src/routes/comment.js | 9 +++++++++ src/server.js | 4 +++- 4 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 src/controllers/comment.js create mode 100644 src/domain/comment.js create mode 100644 src/routes/comment.js diff --git a/src/controllers/comment.js b/src/controllers/comment.js new file mode 100644 index 00000000..75ce4085 --- /dev/null +++ b/src/controllers/comment.js @@ -0,0 +1,7 @@ +import { getCommentsDb } from "../domain/comment.js"; +import { sendDataResponse } from "../utils/responses.js"; + +export const getComments = async (req, res) => { + const comments = await getCommentsDb() + return sendDataResponse(res, 200, {comments}) +} \ No newline at end of file diff --git a/src/domain/comment.js b/src/domain/comment.js new file mode 100644 index 00000000..5940234f --- /dev/null +++ b/src/domain/comment.js @@ -0,0 +1,33 @@ +import dbClient from "../utils/dbClient.js"; + +export async function getCommentsDb() { + const comments = await dbClient.comment.findMany({ + include: { + user: { + include: { + profile: true + } + }, + } + }) + + const newCommentList = comments.map((comment) => { + const profile = comment.user.profile + + const author = { + firstName: profile.firstName, + lastName: profile.lastName + } + + return { + id: comment.id, + content: comment.content, + postId: comment.post.id, + userId: comment.user.id, + createdAt: comment.createdAt, + updatedAt: comment.updatedAt, + author + } +}) + return newCommentList +} \ No newline at end of file diff --git a/src/routes/comment.js b/src/routes/comment.js new file mode 100644 index 00000000..34c5bf0c --- /dev/null +++ b/src/routes/comment.js @@ -0,0 +1,9 @@ +import { Router } from "express"; +import { getComments } from "../controllers/comment.js"; +import { validateAuthentication } from "../middleware/auth.js"; + +const router = Router() + +router.get('/', validateAuthentication, getComments) + +export default router \ No newline at end of file diff --git a/src/server.js b/src/server.js index e875f810..32b64f05 100644 --- a/src/server.js +++ b/src/server.js @@ -10,6 +10,7 @@ import postRouter from './routes/post.js' import authRouter from './routes/auth.js' import cohortRouter from './routes/cohort.js' import deliveryLogRouter from './routes/deliveryLog.js' +import commentRouter from './routes/comment.js' const app = express() app.disable('x-powered-by') @@ -28,6 +29,7 @@ app.use('/posts', postRouter) app.use('/cohorts', cohortRouter) app.use('/logs', deliveryLogRouter) app.use('/', authRouter) +app.use('/comments', commentRouter) app.get('*', (req, res) => { res.status(404).json({ @@ -36,6 +38,6 @@ app.get('*', (req, res) => { resource: 'Not found' } }) -}) +}); export default app From f5a10b471882630d91d784865720feaf1a2e6664 Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Fri, 9 Feb 2024 21:30:15 +0000 Subject: [PATCH 70/86] Add new router /comments --- src/routes/comments.js | 4 ++++ src/server.js | 2 ++ 2 files changed, 6 insertions(+) create mode 100644 src/routes/comments.js diff --git a/src/routes/comments.js b/src/routes/comments.js new file mode 100644 index 00000000..c23d4847 --- /dev/null +++ b/src/routes/comments.js @@ -0,0 +1,4 @@ +import { Router } from 'express' +const router = Router() + +export default router diff --git a/src/server.js b/src/server.js index e875f810..2df8be57 100644 --- a/src/server.js +++ b/src/server.js @@ -9,6 +9,7 @@ import userRouter from './routes/user.js' import postRouter from './routes/post.js' import authRouter from './routes/auth.js' import cohortRouter from './routes/cohort.js' +import commentsRouter from './routes/comments.js' import deliveryLogRouter from './routes/deliveryLog.js' const app = express() @@ -27,6 +28,7 @@ app.use('/users', userRouter) app.use('/posts', postRouter) app.use('/cohorts', cohortRouter) app.use('/logs', deliveryLogRouter) +app.use('/comments', commentsRouter) app.use('/', authRouter) app.get('*', (req, res) => { From 72ecc3f3e0a8a178bf9a81786f99fd19de7392ed Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Fri, 9 Feb 2024 21:32:40 +0000 Subject: [PATCH 71/86] Add comments controller --- src/controllers/comments.js | 1 + src/routes/comments.js | 4 ++++ 2 files changed, 5 insertions(+) create mode 100644 src/controllers/comments.js diff --git a/src/controllers/comments.js b/src/controllers/comments.js new file mode 100644 index 00000000..db55f5ae --- /dev/null +++ b/src/controllers/comments.js @@ -0,0 +1 @@ +export const createComment = async (req, res) => {} diff --git a/src/routes/comments.js b/src/routes/comments.js index c23d4847..23a02ac2 100644 --- a/src/routes/comments.js +++ b/src/routes/comments.js @@ -1,4 +1,8 @@ import { Router } from 'express' +import { createComment } from '../controllers/comments.js' + const router = Router() +router.post('/', createComment) + export default router From 3f07434c729d374f947e7bea8058eb811a098e12 Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Fri, 9 Feb 2024 21:34:27 +0000 Subject: [PATCH 72/86] Update global error handler --- src/server.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server.js b/src/server.js index 2df8be57..c15e1916 100644 --- a/src/server.js +++ b/src/server.js @@ -32,10 +32,10 @@ app.use('/comments', commentsRouter) app.use('/', authRouter) app.get('*', (req, res) => { - res.status(404).json({ + res.status(req.status ?? 500).json({ status: 'fail', data: { - resource: 'Not found' + message: req.message } }) }) From 89a64db685444e8aa12452a01ffb5af062b98331 Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Fri, 9 Feb 2024 21:36:20 +0000 Subject: [PATCH 73/86] Add global error handler --- src/server.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/server.js b/src/server.js index c15e1916..6bff04bc 100644 --- a/src/server.js +++ b/src/server.js @@ -31,11 +31,20 @@ app.use('/logs', deliveryLogRouter) app.use('/comments', commentsRouter) app.use('/', authRouter) +app.use((err, req, res, next) => { + res.status(err.status ?? 500).json({ + status: 'error', + data: { + message: err.message + } + }) +}) + app.get('*', (req, res) => { - res.status(req.status ?? 500).json({ + res.status(404).json({ status: 'fail', data: { - message: req.message + resource: 'Not found' } }) }) From c90916ffbcc6597dce02959ba25f5884224e9275 Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Fri, 9 Feb 2024 21:49:41 +0000 Subject: [PATCH 74/86] Add checkFields error handler --- src/middleware/commentErrors.js | 18 ++++++++++++++++++ src/routes/comments.js | 5 ++++- 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 src/middleware/commentErrors.js diff --git a/src/middleware/commentErrors.js b/src/middleware/commentErrors.js new file mode 100644 index 00000000..88fa29a8 --- /dev/null +++ b/src/middleware/commentErrors.js @@ -0,0 +1,18 @@ +const errorCreator = (message, status) => { + const error = new Error(message) + error.status = status + return error +} + +export const checkFields = async (req, res, next) => { + const fields = req.body + const requiredFields = ['userId', 'postId', 'content'] + + requiredFields.forEach((field) => { + if (!fields[field]) { + throw errorCreator(`Missing field: ${field}`, 400) + } + }) + + next() +} diff --git a/src/routes/comments.js b/src/routes/comments.js index 23a02ac2..8f8dbd61 100644 --- a/src/routes/comments.js +++ b/src/routes/comments.js @@ -1,8 +1,11 @@ import { Router } from 'express' import { createComment } from '../controllers/comments.js' +// Error handlers +import { checkFields } from '../middleware/commentErrors.js' + const router = Router() -router.post('/', createComment) +router.post('/', checkFields, createComment) export default router From 9131cc7b7299a9dc30e15f6b150a31522c30e7f3 Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Fri, 9 Feb 2024 21:52:58 +0000 Subject: [PATCH 75/86] Add createCommentDb --- src/domain/comments.js | 21 +++++++++++++++++++++ src/middleware/commentErrors.js | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 src/domain/comments.js diff --git a/src/domain/comments.js b/src/domain/comments.js new file mode 100644 index 00000000..9adaecf1 --- /dev/null +++ b/src/domain/comments.js @@ -0,0 +1,21 @@ +import dbClient from '../utils/dbClient' + +export const createCommentDb = async ({ userId, postId, content }) => { + const createdComment = await dbClient.comment.create({ + data: { + content, + user: { + content: { + id: Number(userId) + } + }, + post: { + connect: { + id: Number(postId) + } + } + } + }) + + return createdComment +} diff --git a/src/middleware/commentErrors.js b/src/middleware/commentErrors.js index 88fa29a8..08dbb479 100644 --- a/src/middleware/commentErrors.js +++ b/src/middleware/commentErrors.js @@ -4,7 +4,7 @@ const errorCreator = (message, status) => { return error } -export const checkFields = async (req, res, next) => { +export const checkFields = (req, res, next) => { const fields = req.body const requiredFields = ['userId', 'postId', 'content'] From 62a866020220516ffdf05c80b2e95448a749eb20 Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Fri, 9 Feb 2024 21:56:06 +0000 Subject: [PATCH 76/86] Add comment controller createComment --- src/controllers/comments.js | 13 ++++++++++++- src/domain/comments.js | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/controllers/comments.js b/src/controllers/comments.js index db55f5ae..8a7d7968 100644 --- a/src/controllers/comments.js +++ b/src/controllers/comments.js @@ -1 +1,12 @@ -export const createComment = async (req, res) => {} +import { sendDataResponse } from '../utils/responses.js' + +// DB +import { createCommentDb } from '../domain/comments.js' + +export const createComment = async (req, res) => { + const data = req.body + + const createdComment = await createCommentDb(data) + + return sendDataResponse(res, 201, createdComment) +} diff --git a/src/domain/comments.js b/src/domain/comments.js index 9adaecf1..a009e74f 100644 --- a/src/domain/comments.js +++ b/src/domain/comments.js @@ -1,4 +1,4 @@ -import dbClient from '../utils/dbClient' +import dbClient from '../utils/dbClient.js' export const createCommentDb = async ({ userId, postId, content }) => { const createdComment = await dbClient.comment.create({ From 839eaa3a38e6bcc27b4f2b3d447f46028e57771c Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Fri, 9 Feb 2024 22:01:10 +0000 Subject: [PATCH 77/86] Fix problem in domain comments file --- src/domain/comments.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/domain/comments.js b/src/domain/comments.js index a009e74f..ab82c921 100644 --- a/src/domain/comments.js +++ b/src/domain/comments.js @@ -5,7 +5,7 @@ export const createCommentDb = async ({ userId, postId, content }) => { data: { content, user: { - content: { + connect: { id: Number(userId) } }, From 0a1bdde249ddd8332106ed35b1afda5281425e1f Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Fri, 9 Feb 2024 22:07:12 +0000 Subject: [PATCH 78/86] Add validate authentication --- src/controllers/comments.js | 7 +++++-- src/middleware/commentErrors.js | 2 +- src/routes/comments.js | 3 ++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/controllers/comments.js b/src/controllers/comments.js index 8a7d7968..67348ebc 100644 --- a/src/controllers/comments.js +++ b/src/controllers/comments.js @@ -4,9 +4,12 @@ import { sendDataResponse } from '../utils/responses.js' import { createCommentDb } from '../domain/comments.js' export const createComment = async (req, res) => { - const data = req.body + const { postId, content } = req.body + const userId = req.user.id - const createdComment = await createCommentDb(data) + console.log(req.user) + + const createdComment = await createCommentDb({ userId, postId, content }) return sendDataResponse(res, 201, createdComment) } diff --git a/src/middleware/commentErrors.js b/src/middleware/commentErrors.js index 08dbb479..0479ea40 100644 --- a/src/middleware/commentErrors.js +++ b/src/middleware/commentErrors.js @@ -6,7 +6,7 @@ const errorCreator = (message, status) => { export const checkFields = (req, res, next) => { const fields = req.body - const requiredFields = ['userId', 'postId', 'content'] + const requiredFields = ['postId', 'content'] requiredFields.forEach((field) => { if (!fields[field]) { diff --git a/src/routes/comments.js b/src/routes/comments.js index 8f8dbd61..69aba54d 100644 --- a/src/routes/comments.js +++ b/src/routes/comments.js @@ -3,9 +3,10 @@ import { createComment } from '../controllers/comments.js' // Error handlers import { checkFields } from '../middleware/commentErrors.js' +import { validateAuthentication } from '../middleware/auth.js' const router = Router() -router.post('/', checkFields, createComment) +router.post('/', validateAuthentication, checkFields, createComment) export default router From 6f8bc7d01462bd3475bdb7ca462670ad47ca67f9 Mon Sep 17 00:00:00 2001 From: LAVINIABENZAR Date: Sun, 11 Feb 2024 13:07:39 -0800 Subject: [PATCH 79/86] prettier fix --- src/controllers/comment.js | 10 +++---- src/domain/comment.js | 54 +++++++++++++++++++------------------- src/routes/comment.js | 8 +++--- src/server.js | 2 +- 4 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/controllers/comment.js b/src/controllers/comment.js index 75ce4085..f8b112f4 100644 --- a/src/controllers/comment.js +++ b/src/controllers/comment.js @@ -1,7 +1,7 @@ -import { getCommentsDb } from "../domain/comment.js"; -import { sendDataResponse } from "../utils/responses.js"; +import { getCommentsDb } from '../domain/comment.js' +import { sendDataResponse } from '../utils/responses.js' export const getComments = async (req, res) => { - const comments = await getCommentsDb() - return sendDataResponse(res, 200, {comments}) -} \ No newline at end of file + const comments = await getCommentsDb() + return sendDataResponse(res, 200, { comments }) +} diff --git a/src/domain/comment.js b/src/domain/comment.js index 5940234f..38dcdc69 100644 --- a/src/domain/comment.js +++ b/src/domain/comment.js @@ -1,33 +1,33 @@ -import dbClient from "../utils/dbClient.js"; +import dbClient from '../utils/dbClient.js' -export async function getCommentsDb() { - const comments = await dbClient.comment.findMany({ +export async function getCommentsDb() { + const comments = await dbClient.comment.findMany({ include: { - user: { - include: { - profile: true - } - }, + user: { + include: { + profile: true + } + } } - }) + }) - const newCommentList = comments.map((comment) => { - const profile = comment.user.profile + const newCommentList = comments.map((comment) => { + const profile = comment.user.profile - const author = { - firstName: profile.firstName, - lastName: profile.lastName - } + const author = { + firstName: profile.firstName, + lastName: profile.lastName + } - return { - id: comment.id, - content: comment.content, - postId: comment.post.id, - userId: comment.user.id, - createdAt: comment.createdAt, - updatedAt: comment.updatedAt, - author - } -}) - return newCommentList -} \ No newline at end of file + return { + id: comment.id, + content: comment.content, + postId: comment.post.id, + userId: comment.user.id, + createdAt: comment.createdAt, + updatedAt: comment.updatedAt, + author + } + }) + return newCommentList +} diff --git a/src/routes/comment.js b/src/routes/comment.js index 34c5bf0c..921f0800 100644 --- a/src/routes/comment.js +++ b/src/routes/comment.js @@ -1,9 +1,9 @@ -import { Router } from "express"; -import { getComments } from "../controllers/comment.js"; -import { validateAuthentication } from "../middleware/auth.js"; +import { Router } from 'express' +import { getComments } from '../controllers/comment.js' +import { validateAuthentication } from '../middleware/auth.js' const router = Router() router.get('/', validateAuthentication, getComments) -export default router \ No newline at end of file +export default router diff --git a/src/server.js b/src/server.js index 32b64f05..51d17e40 100644 --- a/src/server.js +++ b/src/server.js @@ -38,6 +38,6 @@ app.get('*', (req, res) => { resource: 'Not found' } }) -}); +}) export default app From d86486095d7ff21640f8f60d69a4bcce117e2c79 Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Mon, 12 Feb 2024 11:06:21 +0000 Subject: [PATCH 80/86] Delete console log in comments controller --- src/controllers/comments.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/controllers/comments.js b/src/controllers/comments.js index 67348ebc..e6cb241d 100644 --- a/src/controllers/comments.js +++ b/src/controllers/comments.js @@ -7,8 +7,6 @@ export const createComment = async (req, res) => { const { postId, content } = req.body const userId = req.user.id - console.log(req.user) - const createdComment = await createCommentDb({ userId, postId, content }) return sendDataResponse(res, 201, createdComment) From bd88f9979dc86ca9e20731309690b279ba8e383a Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Mon, 12 Feb 2024 11:10:20 +0000 Subject: [PATCH 81/86] Update checkFields function --- src/middleware/commentErrors.js | 19 ++++++++++--------- src/routes/comments.js | 7 ++++++- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/middleware/commentErrors.js b/src/middleware/commentErrors.js index 0479ea40..6c0463da 100644 --- a/src/middleware/commentErrors.js +++ b/src/middleware/commentErrors.js @@ -4,15 +4,16 @@ const errorCreator = (message, status) => { return error } -export const checkFields = (req, res, next) => { - const fields = req.body - const requiredFields = ['postId', 'content'] +export const checkFields = (requiredFields) => { + return (req, res, next) => { + const fields = req.body - requiredFields.forEach((field) => { - if (!fields[field]) { - throw errorCreator(`Missing field: ${field}`, 400) - } - }) + requiredFields.forEach((field) => { + if (!fields[field]) { + throw errorCreator(`Missing field: ${field}`, 400) + } + }) - next() + next() + } } diff --git a/src/routes/comments.js b/src/routes/comments.js index 69aba54d..4088b5f7 100644 --- a/src/routes/comments.js +++ b/src/routes/comments.js @@ -7,6 +7,11 @@ import { validateAuthentication } from '../middleware/auth.js' const router = Router() -router.post('/', validateAuthentication, checkFields, createComment) +router.post( + '/', + validateAuthentication, + checkFields(['postId', 'content']), + createComment +) export default router From 23230e24599dec8aad76b110cca0648044a619dc Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Mon, 12 Feb 2024 12:35:05 +0000 Subject: [PATCH 82/86] Fix issue with likes --- src/domain/post.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/domain/post.js b/src/domain/post.js index f627e1b1..9cdd61ea 100644 --- a/src/domain/post.js +++ b/src/domain/post.js @@ -45,7 +45,7 @@ export async function getPosts() { updatedAt: post.updatedAt, userId: post.user.id, comments: post.comments, - likes: post.likes.length, + likes: post.likes, author: { firstName: profile.firstName, lastName: profile.lastName From f1476ce9f04712db936957f50af3d770ffdf188d Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Mon, 12 Feb 2024 15:14:48 +0000 Subject: [PATCH 83/86] refactor: use class --- src/controllers/comment.js | 5 ++- src/domain/comment.js | 75 +++++++++++++++++++++++++------------- 2 files changed, 53 insertions(+), 27 deletions(-) diff --git a/src/controllers/comment.js b/src/controllers/comment.js index f8b112f4..cf601c5e 100644 --- a/src/controllers/comment.js +++ b/src/controllers/comment.js @@ -1,7 +1,8 @@ -import { getCommentsDb } from '../domain/comment.js' +import Comment from '../domain/comment.js' import { sendDataResponse } from '../utils/responses.js' export const getComments = async (req, res) => { - const comments = await getCommentsDb() + console.log('ran') + const comments = await Comment.getAll() return sendDataResponse(res, 200, { comments }) } diff --git a/src/domain/comment.js b/src/domain/comment.js index 38dcdc69..052e0653 100644 --- a/src/domain/comment.js +++ b/src/domain/comment.js @@ -1,33 +1,58 @@ import dbClient from '../utils/dbClient.js' -export async function getCommentsDb() { - const comments = await dbClient.comment.findMany({ - include: { - user: { - include: { - profile: true +export default class Comment { + /** + * @param { {id: int, content; string, postId: int, userId: int, user: { profile: {firstName: string, lastName: string }} createdAt: dateTime, updatedAt: dateTime } } comment + * @returns {id: int, content; string, postId: int, userId: int, author: {firstName: string, lastName: string } createdAt: dateTime, updatedAt: dateTime } + */ + constructor(id, content, postId, post, userId, user, createdAt, updatedAt) { + this.id = id + this.content = content + this.postId = postId + this.post = post + this.userId = userId + this.user = user + this.createdAt = createdAt + this.updatedAt = updatedAt + } + + static async _findMany() { + const comments = await dbClient.comment.findMany({ + include: { + user: { + include: { + profile: true + } + }, + post: { + select: { id: true } } } - } - }) + }) + return comments + } + + static async getAll() { + const comments = await Comment._findMany() - const newCommentList = comments.map((comment) => { - const profile = comment.user.profile + const newCommentList = comments.map((comment) => { + const profile = comment.user.profile - const author = { - firstName: profile.firstName, - lastName: profile.lastName - } + const author = { + firstName: profile.firstName, + lastName: profile.lastName + } - return { - id: comment.id, - content: comment.content, - postId: comment.post.id, - userId: comment.user.id, - createdAt: comment.createdAt, - updatedAt: comment.updatedAt, - author - } - }) - return newCommentList + return { + id: comment.id, + content: comment.content, + postId: comment.post.id, + userId: comment.user.id, + createdAt: comment.createdAt, + updatedAt: comment.updatedAt, + author + } + }) + return newCommentList + } } From 0cdb26911dd44c27beba487766ad97dfd3388b56 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Mon, 12 Feb 2024 15:53:29 +0000 Subject: [PATCH 84/86] refactor: getALl returns class instances --- src/domain/comment.js | 41 +++++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/src/domain/comment.js b/src/domain/comment.js index 052e0653..cb0922c1 100644 --- a/src/domain/comment.js +++ b/src/domain/comment.js @@ -5,17 +5,32 @@ export default class Comment { * @param { {id: int, content; string, postId: int, userId: int, user: { profile: {firstName: string, lastName: string }} createdAt: dateTime, updatedAt: dateTime } } comment * @returns {id: int, content; string, postId: int, userId: int, author: {firstName: string, lastName: string } createdAt: dateTime, updatedAt: dateTime } */ - constructor(id, content, postId, post, userId, user, createdAt, updatedAt) { + constructor(id, content, postId, userId, author, createdAt, updatedAt) { this.id = id this.content = content this.postId = postId - this.post = post this.userId = userId - this.user = user + this.author = author this.createdAt = createdAt this.updatedAt = updatedAt } + static fromDb(comment) { + const author = { + firstName: comment.user.profile.firstName, + lastName: comment.user.profile.lastName + } + return new Comment( + comment.id, + comment.content, + comment.post.id, + comment.user.id, + comment.createdAt, + author, + comment.updatedAt + ) + } + static async _findMany() { const comments = await dbClient.comment.findMany({ include: { @@ -34,25 +49,7 @@ export default class Comment { static async getAll() { const comments = await Comment._findMany() - - const newCommentList = comments.map((comment) => { - const profile = comment.user.profile - - const author = { - firstName: profile.firstName, - lastName: profile.lastName - } - - return { - id: comment.id, - content: comment.content, - postId: comment.post.id, - userId: comment.user.id, - createdAt: comment.createdAt, - updatedAt: comment.updatedAt, - author - } - }) + const newCommentList = comments.map(Comment.fromDb) return newCommentList } } From dbcae44042ebb62878e3cf5120d907d335b663c1 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Mon, 12 Feb 2024 16:19:09 +0000 Subject: [PATCH 85/86] docs: add comments get endpoint --- docs/openapi.yml | 56 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 3 deletions(-) diff --git a/docs/openapi.yml b/docs/openapi.yml index c89bcde6..6b8e923c 100644 --- a/docs/openapi.yml +++ b/docs/openapi.yml @@ -88,7 +88,6 @@ paths: '400': description: Invalid username/password supplied - /users/{id}: get: tags: @@ -345,7 +344,22 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' - + /comments: + get: + tags: + - comments + summary: Retrieve comments + description: only registered users can view these + operationId: getComments + security: + - bearerAuth: [] + responses: + 200: + description: success + content: + application/json: + schema: + $ref: '#/components/schemas/Comments' components: securitySchemes: bearerAuth: @@ -499,6 +513,13 @@ components: content: type: string format: string + author: + type: object + properties: + firstName: + type: string + lastName: + type: string createdAt: type: string format: string @@ -521,7 +542,36 @@ components: format: string updatedAt: type: string - + + Comments: + type: object + properties: + status: + type: string + data: + type: object + properties: + comments: + type: array + items: + type: object + properties: + id: + type: integer + content: + type: string + postId: + type: integer + userId: + type: integer + author: + type: object + properties: + firstName: + type: string + lastName: + type: string + CreatedUser: type: object properties: From edcfc04c2846a600b7e30e2861a4fb3b000e8015 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Mon, 12 Feb 2024 16:24:50 +0000 Subject: [PATCH 86/86] chore: remove console.log --- src/controllers/comment.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/controllers/comment.js b/src/controllers/comment.js index cf601c5e..eb760e17 100644 --- a/src/controllers/comment.js +++ b/src/controllers/comment.js @@ -2,7 +2,6 @@ import Comment from '../domain/comment.js' import { sendDataResponse } from '../utils/responses.js' export const getComments = async (req, res) => { - console.log('ran') const comments = await Comment.getAll() return sendDataResponse(res, 200, { comments }) }