From 1a122ef1af4b61f60f455d053a9d08fd07c01182 Mon Sep 17 00:00:00 2001 From: Luca Date: Fri, 2 Feb 2024 17:14:40 +0100 Subject: [PATCH 001/216] 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 002/216] 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 003/216] 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 004/216] 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 005/216] 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 006/216] 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 007/216] 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 008/216] 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 009/216] 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 010/216] 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 011/216] 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 012/216] 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 013/216] 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 014/216] 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 015/216] 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 016/216] 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 017/216] 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 018/216] 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 019/216] 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 020/216] 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 021/216] 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 022/216] 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 023/216] 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 024/216] 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 025/216] 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 026/216] 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 027/216] 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 028/216] 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 029/216] 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 030/216] 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 031/216] 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 032/216] 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 033/216] 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 034/216] 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 035/216] 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 036/216] 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 037/216] 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 038/216] 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 039/216] 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 040/216] 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 041/216] 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 042/216] 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 043/216] 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 044/216] 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 045/216] 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 046/216] 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 047/216] 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 048/216] 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 049/216] 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 050/216] 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 051/216] 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 052/216] 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 053/216] 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 054/216] 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 055/216] 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 056/216] 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 057/216] 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 058/216] 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 059/216] 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 060/216] 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 061/216] 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 062/216] 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 063/216] 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 064/216] 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 065/216] 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 066/216] 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 067/216] 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 068/216] 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 069/216] 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 070/216] 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 071/216] 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 072/216] 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 073/216] 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 074/216] 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 075/216] 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 076/216] 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 077/216] 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 078/216] 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 079/216] 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 080/216] 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 081/216] 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 082/216] 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 083/216] 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 084/216] 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 008586ac69d0cdf04c6604151f97684bf6efe618 Mon Sep 17 00:00:00 2001 From: LAVINIABENZAR Date: Mon, 12 Feb 2024 08:10:22 -0800 Subject: [PATCH 085/216] name property added to cohort model and the seed.js file --- .../20240212125430_cohort_name_property/migration.sql | 8 ++++++++ prisma/schema.prisma | 1 + prisma/seed.js | 7 ++++++- 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 prisma/migrations/20240212125430_cohort_name_property/migration.sql diff --git a/prisma/migrations/20240212125430_cohort_name_property/migration.sql b/prisma/migrations/20240212125430_cohort_name_property/migration.sql new file mode 100644 index 00000000..7ca75017 --- /dev/null +++ b/prisma/migrations/20240212125430_cohort_name_property/migration.sql @@ -0,0 +1,8 @@ +/* + Warnings: + + - Added the required column `name` to the `Cohort` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "Cohort" ADD COLUMN "name" TEXT NOT NULL; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 6824ac6b..ed9de9aa 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -46,6 +46,7 @@ model Profile { model Cohort { id Int @id @default(autoincrement()) + name String users User[] deliveryLogs DeliveryLog[] createdAt DateTime @default(now()) diff --git a/prisma/seed.js b/prisma/seed.js index 70df48ff..3dd200f2 100644 --- a/prisma/seed.js +++ b/prisma/seed.js @@ -65,7 +65,12 @@ async function createPost(userId, content, comments, likes) { async function createCohort() { const cohort = await prisma.cohort.create({ - data: {} + data: { + name: 'Cohort 4' + }, + include: { + users: true + } }) console.info('Cohort created', cohort) From dbcae44042ebb62878e3cf5120d907d335b663c1 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Mon, 12 Feb 2024 16:19:09 +0000 Subject: [PATCH 086/216] 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 087/216] 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 }) } From 72bf126ef40861d37183c4049dafb4ebedcf5bbf Mon Sep 17 00:00:00 2001 From: LAVINIABENZAR Date: Mon, 12 Feb 2024 09:36:54 -0800 Subject: [PATCH 088/216] updated api docs --- docs/openapi.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/openapi.yml b/docs/openapi.yml index c89bcde6..ef41a19c 100644 --- a/docs/openapi.yml +++ b/docs/openapi.yml @@ -372,6 +372,9 @@ components: properties: id: type: integer + name: + type: string + format: string createdAt: type: string format: string From 474b01a04335680edda7d49ac73c3e4c7b80cba1 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Mon, 12 Feb 2024 17:41:19 +0000 Subject: [PATCH 089/216] docs: add post comment endpoint --- docs/openapi.yml | 63 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/docs/openapi.yml b/docs/openapi.yml index 6b8e923c..e901fb3f 100644 --- a/docs/openapi.yml +++ b/docs/openapi.yml @@ -354,12 +354,34 @@ paths: security: - bearerAuth: [] responses: - 200: + '200': description: success content: application/json: schema: $ref: '#/components/schemas/Comments' + post: + tags: + - comments + summary: Create a new comment + description: only registered users can do this + operationId: postComment + security: + - bearerAuth: [] + responses: + '201': + description: success + content: + application/json: + schema: + $ref: '#/components/schemas/Comment' + '400': + description: fail + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + components: securitySchemes: bearerAuth: @@ -571,6 +593,45 @@ components: type: string lastName: type: string + createdAt: + type: string + format: string + updatedAt: + type: string + format: string + + Comment: + type: object + properties: + status: + type: string + data: + type: object + properties: + comment: + 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 + createdAt: + type: string + format: string + updatedAt: + type: string + format: string CreatedUser: type: object From 28198e2fefef6e1f2479ad258ecd383adee3d376 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Mon, 12 Feb 2024 17:41:59 +0000 Subject: [PATCH 090/216] refactor: delete redundant comments.js files --- src/controllers/comments.js | 13 ------------- src/domain/comments.js | 21 --------------------- src/routes/comments.js | 17 ----------------- 3 files changed, 51 deletions(-) delete mode 100644 src/controllers/comments.js delete mode 100644 src/domain/comments.js delete mode 100644 src/routes/comments.js diff --git a/src/controllers/comments.js b/src/controllers/comments.js deleted file mode 100644 index e6cb241d..00000000 --- a/src/controllers/comments.js +++ /dev/null @@ -1,13 +0,0 @@ -import { sendDataResponse } from '../utils/responses.js' - -// DB -import { createCommentDb } from '../domain/comments.js' - -export const createComment = async (req, res) => { - const { postId, content } = req.body - const userId = req.user.id - - const createdComment = await createCommentDb({ userId, postId, content }) - - return sendDataResponse(res, 201, createdComment) -} diff --git a/src/domain/comments.js b/src/domain/comments.js deleted file mode 100644 index ab82c921..00000000 --- a/src/domain/comments.js +++ /dev/null @@ -1,21 +0,0 @@ -import dbClient from '../utils/dbClient.js' - -export const createCommentDb = async ({ userId, postId, content }) => { - const createdComment = await dbClient.comment.create({ - data: { - content, - user: { - connect: { - id: Number(userId) - } - }, - post: { - connect: { - id: Number(postId) - } - } - } - }) - - return createdComment -} diff --git a/src/routes/comments.js b/src/routes/comments.js deleted file mode 100644 index 4088b5f7..00000000 --- a/src/routes/comments.js +++ /dev/null @@ -1,17 +0,0 @@ -import { Router } from 'express' -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( - '/', - validateAuthentication, - checkFields(['postId', 'content']), - createComment -) - -export default router From a75aa49fd59588c30c5e1bb611ab429830060f70 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Mon, 12 Feb 2024 17:42:28 +0000 Subject: [PATCH 091/216] refactor: delete redundant router --- src/server.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/server.js b/src/server.js index e2e55e8b..76855e64 100644 --- a/src/server.js +++ b/src/server.js @@ -9,7 +9,6 @@ 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' import commentRouter from './routes/comment.js' @@ -29,7 +28,6 @@ app.use('/users', userRouter) app.use('/posts', postRouter) app.use('/cohorts', cohortRouter) app.use('/logs', deliveryLogRouter) -app.use('/comments', commentsRouter) app.use('/', authRouter) app.use('/comments', commentRouter) From 41b6437fc8d1e6e8d7eae5538bef698af1a0bafa Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Mon, 12 Feb 2024 17:43:32 +0000 Subject: [PATCH 092/216] refactor: move post route to comment.js --- src/routes/comment.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/routes/comment.js b/src/routes/comment.js index 921f0800..e0b266b2 100644 --- a/src/routes/comment.js +++ b/src/routes/comment.js @@ -1,9 +1,16 @@ import { Router } from 'express' -import { getComments } from '../controllers/comment.js' +import { createComment, getComments } from '../controllers/comment.js' import { validateAuthentication } from '../middleware/auth.js' +import { checkFields } from '../middleware/commentErrors.js' const router = Router() router.get('/', validateAuthentication, getComments) +router.post( + '/', + validateAuthentication, + checkFields(['postId', 'content']), + createComment +) export default router From 280218be8b64dcd1d40d5b6d7bf7e1732369f986 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Mon, 12 Feb 2024 17:46:37 +0000 Subject: [PATCH 093/216] refactor: rewrite createCommentDb() as class method save() --- src/domain/comment.js | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/domain/comment.js b/src/domain/comment.js index cb0922c1..af7e749e 100644 --- a/src/domain/comment.js +++ b/src/domain/comment.js @@ -31,6 +31,11 @@ export default class Comment { ) } + static async fromJson(json) { + console.log(json) + return new Comment(null, json.content, json.postId, json.userId) + } + static async _findMany() { const comments = await dbClient.comment.findMany({ include: { @@ -47,9 +52,43 @@ export default class Comment { return comments } + async save() { + const data = { + content: this.content, + author: this.author, + user: { + connect: { + id: Number(this.userId) + } + }, + post: { + connect: { + id: Number(this.postId) + } + } + } + const createdComment = await dbClient.comment.create({ + data, + include: { + user: { + include: { + profile: true + } + }, + post: true + } + }) + return Comment.fromDb(createdComment) + } + static async getAll() { const comments = await Comment._findMany() const newCommentList = comments.map(Comment.fromDb) return newCommentList } + + static async createComment() { + const comment = await Comment._create(Comment.fromJSON()) + return Comment.fromDb(comment) + } } From 388dc25b21f6f4ab18020eb27a8c808b23564c87 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Mon, 12 Feb 2024 17:47:51 +0000 Subject: [PATCH 094/216] refactor: rewrite createComment() to use Comment class methods --- src/controllers/comment.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/controllers/comment.js b/src/controllers/comment.js index eb760e17..e564cf05 100644 --- a/src/controllers/comment.js +++ b/src/controllers/comment.js @@ -5,3 +5,13 @@ export const getComments = async (req, res) => { const comments = await Comment.getAll() return sendDataResponse(res, 200, { comments }) } + +export const createComment = async (req, res) => { + const json = req.body + json.userId = req.user.id + + const commentToCreate = await Comment.fromJson(req.body) + const createdComment = await commentToCreate.save() + + return sendDataResponse(res, 201, createdComment) +} From c130d2a052f55e282f03dbc0f7eab572a217d309 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Mon, 12 Feb 2024 17:49:25 +0000 Subject: [PATCH 095/216] chore: remove console.log --- src/domain/comment.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/domain/comment.js b/src/domain/comment.js index af7e749e..4043e710 100644 --- a/src/domain/comment.js +++ b/src/domain/comment.js @@ -32,7 +32,6 @@ export default class Comment { } static async fromJson(json) { - console.log(json) return new Comment(null, json.content, json.postId, json.userId) } From ea695f2de4286480345bb36745b9f3ec87d150e0 Mon Sep 17 00:00:00 2001 From: ilham saleh Date: Tue, 13 Feb 2024 10:53:59 +0000 Subject: [PATCH 096/216] ... --- src/controllers/auth.js | 4 ++-- src/controllers/user.js | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/controllers/auth.js b/src/controllers/auth.js index 9a376a73..a1dfb734 100644 --- a/src/controllers/auth.js +++ b/src/controllers/auth.js @@ -9,7 +9,7 @@ export const login = async (req, res) => { if (!email) { return sendDataResponse(res, 400, { - email: 'Invalid email and/or password provided' + error: 'Invalid email and/or password provided' }) } @@ -19,7 +19,7 @@ export const login = async (req, res) => { if (!areCredentialsValid) { return sendDataResponse(res, 400, { - email: 'Invalid email and/or password provided' + error: 'Invalid email and/or password provided' }) } diff --git a/src/controllers/user.js b/src/controllers/user.js index 284adae4..9b03467b 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -8,7 +8,7 @@ export const create = async (req, res) => { const existingUser = await User.findByEmail(userToCreate.email) if (existingUser) { - return sendDataResponse(res, 409, { email: 'Email already in use' }) + return sendDataResponse(res, 409, { error: 'Email already in use' }) } const createdUser = await userToCreate.save() @@ -27,7 +27,7 @@ export const getById = async (req, res) => { const foundUser = await User.findById(id) if (!foundUser) { - return sendDataResponse(res, 404, { id: 'User not found' }) + return sendDataResponse(res, 404, { message: 'User not found' }) } return sendDataResponse(res, 200, foundUser) @@ -59,7 +59,7 @@ export const updateById = async (req, res) => { const { cohort_id: cohortId } = req.body if (!cohortId) { - return sendDataResponse(res, 400, { cohort_id: 'Cohort ID is required' }) + return sendDataResponse(res, 400, { message: 'Cohort ID is required' }) } return sendDataResponse(res, 201, { user: { cohort_id: cohortId } }) From 4e0357d072c7718d6f3359e1742182550b921d4f Mon Sep 17 00:00:00 2001 From: AllyDouillette <142409831+AllyDouillette@users.noreply.github.com> Date: Tue, 13 Feb 2024 11:56:51 +0100 Subject: [PATCH 097/216] new route and controller function --- src/controllers/user.js | 2 ++ src/routes/user.js | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/controllers/user.js b/src/controllers/user.js index 284adae4..4ce77e37 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -36,6 +36,8 @@ export const getById = async (req, res) => { } } +export const getSelf = async (req, res) => res.json({ self: req.user }) + export const getAll = async (req, res) => { const { name } = req.query diff --git a/src/routes/user.js b/src/routes/user.js index 9f63d162..cfef29f8 100644 --- a/src/routes/user.js +++ b/src/routes/user.js @@ -1,5 +1,11 @@ import { Router } from 'express' -import { create, getById, getAll, updateById } from '../controllers/user.js' +import { + create, + getById, + getSelf, + getAll, + updateById +} from '../controllers/user.js' import { validateAuthentication, validateTeacherRole @@ -9,6 +15,7 @@ const router = Router() router.post('/', create) router.get('/', validateAuthentication, getAll) +router.get('/me', validateAuthentication, getSelf) router.get('/:id', validateAuthentication, getById) router.patch('/:id', validateAuthentication, validateTeacherRole, updateById) From 9aaf0b892a60f6c7030d1055561db7ae4248ad38 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Tue, 13 Feb 2024 12:20:46 +0000 Subject: [PATCH 098/216] fix: set default values for firstName and lastName profile properties --- src/domain/user.js | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/domain/user.js b/src/domain/user.js index bffa5f77..39985944 100644 --- a/src/domain/user.js +++ b/src/domain/user.js @@ -55,8 +55,8 @@ export default class User { ) { this.id = id this.cohortId = cohortId - this.firstName = firstName - this.lastName = lastName + this.firstName = firstName || 'unknown' + this.lastName = lastName || 'unknown' this.email = email this.bio = bio this.githubUrl = githubUrl @@ -98,16 +98,15 @@ export default class User { } } - if (this.firstName && this.lastName) { - data.profile = { - create: { - firstName: this.firstName, - lastName: this.lastName, - bio: this.bio, - githubUrl: this.githubUrl - } + data.profile = { + create: { + firstName: this.firstName, + lastName: this.lastName, + bio: this.bio, + githubUrl: this.githubUrl } } + const createdUser = await dbClient.user.create({ data, include: { From a99a317bbe9a0a299eca7ea346ba296a10786522 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Tue, 13 Feb 2024 13:22:37 +0000 Subject: [PATCH 099/216] build: add onDelete: Cascade to post field on comment --- prisma/schema.prisma | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index ed9de9aa..2f5bd204 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -78,7 +78,7 @@ model Comment { id Int @id @default(autoincrement()) content String postId Int - post Post @relation(fields: [postId], references: [id]) + post Post @relation(fields: [postId], references: [id], onDelete: Cascade) userId Int user User @relation(fields: [userId], references: [id]) createdAt DateTime @default(now()) From 6799229b58aa343eccc31e5e36c1942e96535dc0 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Tue, 13 Feb 2024 13:23:14 +0000 Subject: [PATCH 100/216] build: generate migrations for cascade on delete --- .../migration.sql | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 prisma/migrations/20240213132101_add_cascade_delete_to_posts_comments/migration.sql diff --git a/prisma/migrations/20240213132101_add_cascade_delete_to_posts_comments/migration.sql b/prisma/migrations/20240213132101_add_cascade_delete_to_posts_comments/migration.sql new file mode 100644 index 00000000..cab7e692 --- /dev/null +++ b/prisma/migrations/20240213132101_add_cascade_delete_to_posts_comments/migration.sql @@ -0,0 +1,5 @@ +-- DropForeignKey +ALTER TABLE "Comment" DROP CONSTRAINT "Comment_postId_fkey"; + +-- AddForeignKey +ALTER TABLE "Comment" ADD CONSTRAINT "Comment_postId_fkey" FOREIGN KEY ("postId") REFERENCES "Post"("id") ON DELETE CASCADE ON UPDATE CASCADE; From 136316d3f8f085a9dbd7d7d03b8365a6f2980359 Mon Sep 17 00:00:00 2001 From: ilham saleh Date: Tue, 13 Feb 2024 15:15:38 +0000 Subject: [PATCH 101/216] replace "message" with "error" when sending errors to front end --- src/controllers/user.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/controllers/user.js b/src/controllers/user.js index 9b03467b..2d17a5c7 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -27,7 +27,7 @@ export const getById = async (req, res) => { const foundUser = await User.findById(id) if (!foundUser) { - return sendDataResponse(res, 404, { message: 'User not found' }) + return sendDataResponse(res, 404, { error: 'User not found' }) } return sendDataResponse(res, 200, foundUser) @@ -59,7 +59,7 @@ export const updateById = async (req, res) => { const { cohort_id: cohortId } = req.body if (!cohortId) { - return sendDataResponse(res, 400, { message: 'Cohort ID is required' }) + return sendDataResponse(res, 400, { error: 'Cohort ID is required' }) } return sendDataResponse(res, 201, { user: { cohort_id: cohortId } }) From 6d91fb55acd4c0b4d9d1503b1cc8d2f0ec6be450 Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Wed, 14 Feb 2024 02:01:11 +0000 Subject: [PATCH 102/216] Add new route /comments/:postId --- src/controllers/comment.js | 2 ++ src/routes/comment.js | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/controllers/comment.js b/src/controllers/comment.js index e564cf05..6aa83e98 100644 --- a/src/controllers/comment.js +++ b/src/controllers/comment.js @@ -15,3 +15,5 @@ export const createComment = async (req, res) => { return sendDataResponse(res, 201, createdComment) } + +export const getCommentsByPost = async (req, res) => {} diff --git a/src/routes/comment.js b/src/routes/comment.js index e0b266b2..35465d25 100644 --- a/src/routes/comment.js +++ b/src/routes/comment.js @@ -1,5 +1,9 @@ import { Router } from 'express' -import { createComment, getComments } from '../controllers/comment.js' +import { + createComment, + getComments, + getCommentsByPost +} from '../controllers/comment.js' import { validateAuthentication } from '../middleware/auth.js' import { checkFields } from '../middleware/commentErrors.js' @@ -12,5 +16,6 @@ router.post( checkFields(['postId', 'content']), createComment ) +router.get('/:postId', getCommentsByPost) export default router From 8ed54654cf42205e1367a5ccc9ca8dd659b27cd5 Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Wed, 14 Feb 2024 02:08:09 +0000 Subject: [PATCH 103/216] Add new error hadler checkPostExist --- src/domain/post.js | 10 ++++++++++ src/middleware/commentErrors.js | 14 ++++++++++++++ src/routes/comment.js | 4 ++-- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/domain/post.js b/src/domain/post.js index 9cdd61ea..89c9f80f 100644 --- a/src/domain/post.js +++ b/src/domain/post.js @@ -108,3 +108,13 @@ export async function toggleLike(postId, userId) { }) return 'Like added successfully.' } + +export const getPostByIdDb = async (postId) => { + const foundPost = await dbClient.post.findFirst({ + where: { + id: Number(postId) + } + }) + + return foundPost +} diff --git a/src/middleware/commentErrors.js b/src/middleware/commentErrors.js index 6c0463da..8b5ca925 100644 --- a/src/middleware/commentErrors.js +++ b/src/middleware/commentErrors.js @@ -1,3 +1,5 @@ +import { getPostByIdDb } from '../domain/post.js' + const errorCreator = (message, status) => { const error = new Error(message) error.status = status @@ -17,3 +19,15 @@ export const checkFields = (requiredFields) => { next() } } + +export const checkPostExist = async (req, res, next) => { + const { postId } = req.params + + const foundPost = await getPostByIdDb(postId) + + if (!foundPost) { + throw errorCreator(`Post with provided id ${postId} does not exist`, 404) + } + + next() +} diff --git a/src/routes/comment.js b/src/routes/comment.js index 35465d25..099c073c 100644 --- a/src/routes/comment.js +++ b/src/routes/comment.js @@ -5,7 +5,7 @@ import { getCommentsByPost } from '../controllers/comment.js' import { validateAuthentication } from '../middleware/auth.js' -import { checkFields } from '../middleware/commentErrors.js' +import { checkFields, checkPostExist } from '../middleware/commentErrors.js' const router = Router() @@ -16,6 +16,6 @@ router.post( checkFields(['postId', 'content']), createComment ) -router.get('/:postId', getCommentsByPost) +router.get('/:postId', checkPostExist, getCommentsByPost) export default router From 522383e9b5ed2cf32255d393061b2d30c0833fdc Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Wed, 14 Feb 2024 02:10:25 +0000 Subject: [PATCH 104/216] Add new function getcommentsByPostId --- src/domain/comment.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/domain/comment.js b/src/domain/comment.js index 4043e710..cfe48808 100644 --- a/src/domain/comment.js +++ b/src/domain/comment.js @@ -90,4 +90,14 @@ export default class Comment { const comment = await Comment._create(Comment.fromJSON()) return Comment.fromDb(comment) } + + static async getCommentsByPostId(postId) { + const foundComments = await dbClient.comment.findMany({ + where: { + postId: Number(postId) + } + }) + + return foundComments + } } From 418da3df8807e16484980bdf820360afed3b5526 Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Wed, 14 Feb 2024 02:12:01 +0000 Subject: [PATCH 105/216] Add new controller getcommentsByPost --- src/controllers/comment.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/controllers/comment.js b/src/controllers/comment.js index 6aa83e98..cb618e57 100644 --- a/src/controllers/comment.js +++ b/src/controllers/comment.js @@ -16,4 +16,10 @@ export const createComment = async (req, res) => { return sendDataResponse(res, 201, createdComment) } -export const getCommentsByPost = async (req, res) => {} +export const getCommentsByPost = async (req, res) => { + const { postId } = req.params + + const comments = await Comment.getCommentsByPostId(postId) + + return sendDataResponse(res, 200, { comments }) +} From aeef94c547a08133260cfed079a3107dde7f7d13 Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Wed, 14 Feb 2024 02:23:45 +0000 Subject: [PATCH 106/216] Fix check post exist error handler --- src/middleware/commentErrors.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/middleware/commentErrors.js b/src/middleware/commentErrors.js index 8b5ca925..f5341464 100644 --- a/src/middleware/commentErrors.js +++ b/src/middleware/commentErrors.js @@ -23,11 +23,15 @@ export const checkFields = (requiredFields) => { export const checkPostExist = async (req, res, next) => { const { postId } = req.params - const foundPost = await getPostByIdDb(postId) + try { + const foundPost = await getPostByIdDb(postId) - if (!foundPost) { - throw errorCreator(`Post with provided id ${postId} does not exist`, 404) - } + if (!foundPost) { + throw errorCreator(`Post with provided id ${postId} does not exist`, 404) + } - next() + next() + } catch (err) { + next(err) + } } From 621bcb858bba98a7b4bc80371ba1cefe962760ac Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Wed, 14 Feb 2024 02:25:28 +0000 Subject: [PATCH 107/216] Add validate authentication --- src/routes/comment.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/routes/comment.js b/src/routes/comment.js index 099c073c..c78eec72 100644 --- a/src/routes/comment.js +++ b/src/routes/comment.js @@ -16,6 +16,11 @@ router.post( checkFields(['postId', 'content']), createComment ) -router.get('/:postId', checkPostExist, getCommentsByPost) +router.get( + '/:postId', + validateAuthentication, + checkPostExist, + getCommentsByPost +) export default router From c38dfae826f759cf1913abdd20c2968b1945acb1 Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Wed, 14 Feb 2024 02:27:39 +0000 Subject: [PATCH 108/216] prisma migrate --- prisma/migrations/20240214022729_fix_github_errors/migration.sql | 1 + 1 file changed, 1 insertion(+) create mode 100644 prisma/migrations/20240214022729_fix_github_errors/migration.sql diff --git a/prisma/migrations/20240214022729_fix_github_errors/migration.sql b/prisma/migrations/20240214022729_fix_github_errors/migration.sql new file mode 100644 index 00000000..af5102c8 --- /dev/null +++ b/prisma/migrations/20240214022729_fix_github_errors/migration.sql @@ -0,0 +1 @@ +-- This is an empty migration. \ No newline at end of file From 19ca4315e482891522d45bfc4d6da90fbc566745 Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Wed, 14 Feb 2024 02:50:10 +0000 Subject: [PATCH 109/216] Remove comments from getPosts function --- src/domain/post.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/domain/post.js b/src/domain/post.js index 89c9f80f..16d62138 100644 --- a/src/domain/post.js +++ b/src/domain/post.js @@ -25,7 +25,6 @@ export async function getPosts() { profile: true } }, - comments: true, likes: true } }) @@ -44,7 +43,6 @@ export async function getPosts() { createdAt: post.createdAt, updatedAt: post.updatedAt, userId: post.user.id, - comments: post.comments, likes: post.likes, author: { firstName: profile.firstName, From 061d60980e26335543a60d883da2fe8b02192d7f Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Wed, 14 Feb 2024 10:53:08 +0000 Subject: [PATCH 110/216] docs: add GET/cohorts endpoint --- docs/openapi.yml | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/docs/openapi.yml b/docs/openapi.yml index 3a5e4536..2f1da813 100644 --- a/docs/openapi.yml +++ b/docs/openapi.yml @@ -344,6 +344,22 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' + get: + tags: + - cohort + summary: Get a list of all the /cohorts + description: This can only be done is the logged in user has a role of TEACHER + operationId: getCohorts + security: + - bearerAuth: [] + responses: + '200': + description: success + content: + application/json: + schema: + $ref: '#/components/schemas/Cohorts' + /comments: get: tags: @@ -417,6 +433,31 @@ components: updatedAt: type: string format: string + + Cohorts: + type: object + properties: + status: + type: string + data: + type: object + properties: + cohorts: + type: array + items: + type: object + properties: + id: + type: integer + name: + type: string + format: string + createdAt: + type: string + format: string + updatedAt: + type: string + format: string AllUsers: type: object From 64a093cd8496f8e5d52f0452940ac7748a6a101e Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Wed, 14 Feb 2024 10:53:36 +0000 Subject: [PATCH 111/216] feat: add GET/cohorts router --- src/routes/cohort.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/routes/cohort.js b/src/routes/cohort.js index 3cc7813d..1c1b23c2 100644 --- a/src/routes/cohort.js +++ b/src/routes/cohort.js @@ -1,5 +1,5 @@ import { Router } from 'express' -import { create } from '../controllers/cohort.js' +import { create, getCohorts } from '../controllers/cohort.js' import { validateAuthentication, validateTeacherRole @@ -8,5 +8,6 @@ import { const router = Router() router.post('/', validateAuthentication, validateTeacherRole, create) +router.get('/', validateAuthentication, validateTeacherRole, getCohorts) export default router From 6c6f0137c5424f42cd6120556a363b831f6b7cfa Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Wed, 14 Feb 2024 10:54:09 +0000 Subject: [PATCH 112/216] feat: add GET/cohorts controller --- src/controllers/cohort.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/controllers/cohort.js b/src/controllers/cohort.js index cc39365b..f3b12639 100644 --- a/src/controllers/cohort.js +++ b/src/controllers/cohort.js @@ -1,4 +1,4 @@ -import { createCohort } from '../domain/cohort.js' +import { createCohort, Cohort } from '../domain/cohort.js' import { sendDataResponse, sendMessageResponse } from '../utils/responses.js' export const create = async (req, res) => { @@ -10,3 +10,12 @@ export const create = async (req, res) => { return sendMessageResponse(res, 500, 'Unable to create cohort') } } + +export const getCohorts = async (req, res) => { + try { + const foundCohorts = await Cohort.getAll() + return sendDataResponse(res, 200, foundCohorts) + } catch (e) { + sendMessageResponse(res, 500, 'Unable to get the list of cohorts') + } +} From 8df2805e0526b110a84fcf7e1105c1ef48bec771 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Wed, 14 Feb 2024 10:54:39 +0000 Subject: [PATCH 113/216] feat: add GET/cohorts domain - update Cohort class --- src/domain/cohort.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/domain/cohort.js b/src/domain/cohort.js index abdda73b..22547d53 100644 --- a/src/domain/cohort.js +++ b/src/domain/cohort.js @@ -13,8 +13,24 @@ export async function createCohort() { } export class Cohort { - constructor(id = null) { + constructor(id = null, name = 'default name', users = []) { this.id = id + this.name = name + this.users = users + } + + static fromDb(cohort) { + return new Cohort(cohort) + } + + static async _findMany() { + return dbClient.cohort.findMany() + } + + static async getAll() { + const foundCohorts = await Cohort._findMany() + const cohortList = foundCohorts.map(Cohort.fromDb) + return cohortList } toJSON() { From 078c779024ca55c7a878874a9084b5f78fa2b3a9 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Wed, 14 Feb 2024 11:08:09 +0000 Subject: [PATCH 114/216] feat: add console.log() for error --- src/controllers/cohort.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/controllers/cohort.js b/src/controllers/cohort.js index f3b12639..cb678b12 100644 --- a/src/controllers/cohort.js +++ b/src/controllers/cohort.js @@ -16,6 +16,7 @@ export const getCohorts = async (req, res) => { const foundCohorts = await Cohort.getAll() return sendDataResponse(res, 200, foundCohorts) } catch (e) { - sendMessageResponse(res, 500, 'Unable to get the list of cohorts') + console.log('Error retrieving cohorts', e) + return sendMessageResponse(res, 500, 'Unable to get the list of cohorts') } } From aa0ee59b3f7a266ae5776bec7af3e3c321775486 Mon Sep 17 00:00:00 2001 From: callumhayden Date: Wed, 14 Feb 2024 12:20:32 +0000 Subject: [PATCH 115/216] updated the schema and seed file, so we can add roles for the teachers --- .../20240214120743_add_teacher/migration.sql | 16 +++++ prisma/schema.prisma | 41 ++++++----- prisma/seed.js | 68 +++++++++++-------- 3 files changed, 82 insertions(+), 43 deletions(-) create mode 100644 prisma/migrations/20240214120743_add_teacher/migration.sql diff --git a/prisma/migrations/20240214120743_add_teacher/migration.sql b/prisma/migrations/20240214120743_add_teacher/migration.sql new file mode 100644 index 00000000..149adc61 --- /dev/null +++ b/prisma/migrations/20240214120743_add_teacher/migration.sql @@ -0,0 +1,16 @@ +-- CreateTable +CREATE TABLE "Teacher" ( + "id" SERIAL NOT NULL, + "userId" INTEGER NOT NULL, + "department" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Teacher_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "Teacher_userId_key" ON "Teacher"("userId"); + +-- AddForeignKey +ALTER TABLE "Teacher" ADD CONSTRAINT "Teacher_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 2f5bd204..cc135a77 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -30,6 +30,7 @@ model User { deliveryLogs DeliveryLog[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt + Teacher Teacher? } model Profile { @@ -44,6 +45,15 @@ model Profile { updatedAt DateTime @updatedAt } +model Teacher { + id Int @id @default(autoincrement()) + userId Int @unique + user User @relation(fields: [userId], references: [id]) + department String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + model Cohort { id Int @id @default(autoincrement()) name String @@ -54,33 +64,33 @@ model Cohort { } model Post { - id Int @id @default(autoincrement()) + id Int @id @default(autoincrement()) content String userId Int - user User @relation(fields: [userId], references: [id]) + user User @relation(fields: [userId], references: [id]) comments Comment[] likes Like[] - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + 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]) + 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], onDelete: Cascade) - userId Int - user User @relation(fields: [userId], references: [id]) + id Int @id @default(autoincrement()) + content String + postId Int + post Post @relation(fields: [postId], references: [id], onDelete: Cascade) + userId Int + user User @relation(fields: [userId], references: [id]) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } @@ -105,4 +115,3 @@ model DeliveryLogLine { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } - diff --git a/prisma/seed.js b/prisma/seed.js index 3dd200f2..14f79847 100644 --- a/prisma/seed.js +++ b/prisma/seed.js @@ -5,26 +5,29 @@ const prisma = new PrismaClient() async function seed() { const cohort = await createCohort() - const student = await createUser( + const student = await createUserWithRole( 'student@test.com', 'Testpassword1!', + 'STUDENT', cohort.id, 'Joe', 'Bloggs', 'Hello, world!', - 'student1' + 'https://github.com/student1' ) - const teacher = await createUser( - 'teacher@test.com', - 'Testpassword1!', + console.log(cohort.id) + // Creating teacher users with specific departments + const teacher = await createUserWithRole( + 'rick@test.com', + 'Testpassword2!', + 'TEACHER', null, 'Rick', 'Sanchez', - 'Hello there!', - 'teacher1', - 'TEACHER' + 'Wubba Lubba Dub Dub!', + 'https://github.com/rick', + 'Software Developer' ) - await createPost( student.id, 'My first post!', @@ -78,41 +81,52 @@ async function createCohort() { return cohort } -async function createUser( +async function createUserWithRole( email, password, + role, cohortId, firstName, lastName, bio, githubUrl, - role = 'STUDENT' + department = null ) { - const user = await prisma.user.create({ - data: { - email, - password: await bcrypt.hash(password, 8), - role, - cohortId, - profile: { - create: { - firstName, - lastName, - bio, - githubUrl - } + const hashedPassword = await bcrypt.hash(password, 10) + const userData = { + email, + password: hashedPassword, + role, + cohortId, + profile: { + create: { + firstName, + lastName, + bio, + githubUrl } - }, + } + } + + const user = await prisma.user.create({ + data: userData, include: { profile: true } }) - console.info(`${role} created`, user) + if (role === 'TEACHER' && department) { + await prisma.teacher.create({ + data: { + userId: user.id, + department + } + }) + } + console.info(`${role} created:`, user.email) return user } - seed().catch(async (e) => { console.error(e) await prisma.$disconnect() From 4e3ecb4e05d67bc7bc9a56f70d7466c8205628f2 Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Wed, 14 Feb 2024 12:43:32 +0000 Subject: [PATCH 116/216] Update docs file --- docs/openapi.yml | 238 +++++++++++++++++++++++------------------------ 1 file changed, 114 insertions(+), 124 deletions(-) diff --git a/docs/openapi.yml b/docs/openapi.yml index 3a5e4536..74db5813 100644 --- a/docs/openapi.yml +++ b/docs/openapi.yml @@ -195,65 +195,65 @@ paths: schema: $ref: '#/components/schemas/Error' put: - tags: - - post + tags: + - post summary: Update post description: you can update your own post operationId: editPost security: - - bearerAuth: [] - requestBody: + - bearerAuth: [] + requestBody: description: Updated post object - content: - application/json: + content: + application/json: schema: type: object - properties: - content: + properties: + content: type: string responses: '200': description: success - content: - application/json: - schema: + content: + application/json: + schema: $ref: '#/components/schemas/Post' '400': description: fail - content: + content: application/json: - schema: + schema: $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' + 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 @@ -346,7 +346,7 @@ paths: $ref: '#/components/schemas/Error' /comments: get: - tags: + tags: - comments summary: Retrieve comments description: only registered users can view these @@ -361,7 +361,7 @@ paths: schema: $ref: '#/components/schemas/Comments' post: - tags: + tags: - comments summary: Create a new comment description: only registered users can do this @@ -381,7 +381,24 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' - + + /comments/{postId}: + get: + tags: + - comments + summary: Retrieve comments by post id + description: only registered users can view these + operationId: getCommentsByPostId + security: + - bearerAuth: [] + responses: + '200': + description: success + content: + application/json: + schema: + $ref: '#/components/schemas/Comments' + components: securitySchemes: bearerAuth: @@ -408,9 +425,9 @@ components: properties: id: type: integer - name: + name: type: string - format: string + format: string createdAt: type: string format: string @@ -524,39 +541,12 @@ components: type: string profileImageUrl: type: string - comments: - type: array - items: - type: object - properties: - id: - type: integer - postId: - type: integer - userId: - type: integer - content: - type: string - format: string - author: - type: object - properties: - firstName: - type: string - lastName: - type: string - createdAt: - type: string - format: string - updatedAt: - type: string - format: string - likes: + likes: type: array items: type: object - properties: - id: + properties: + id: type: integer userId: type: integer @@ -569,51 +559,18 @@ components: 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 - createdAt: - type: string - format: string - updatedAt: - type: string - format: string - - Comment: - type: object - properties: - status: - type: string - data: - type: object - properties: - comment: + type: object + properties: + status: + type: string + data: + type: object + properties: + comments: + type: array + items: type: object - properties: + properties: id: type: integer content: @@ -624,8 +581,8 @@ components: type: integer author: type: object - properties: - firstName: + properties: + firstName: type: string lastName: type: string @@ -634,7 +591,40 @@ components: format: string updatedAt: type: string - format: string + format: string + + Comment: + type: object + properties: + status: + type: string + data: + type: object + properties: + comment: + 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 + createdAt: + type: string + format: string + updatedAt: + type: string + format: string CreatedUser: type: object From 70c8763305381e0555df0359b7fbe20ecb767d26 Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Wed, 14 Feb 2024 12:51:10 +0000 Subject: [PATCH 117/216] Remove migration --- prisma/migrations/20240214022729_fix_github_errors/migration.sql | 1 - 1 file changed, 1 deletion(-) delete mode 100644 prisma/migrations/20240214022729_fix_github_errors/migration.sql diff --git a/prisma/migrations/20240214022729_fix_github_errors/migration.sql b/prisma/migrations/20240214022729_fix_github_errors/migration.sql deleted file mode 100644 index af5102c8..00000000 --- a/prisma/migrations/20240214022729_fix_github_errors/migration.sql +++ /dev/null @@ -1 +0,0 @@ --- This is an empty migration. \ No newline at end of file From 083dfb7116e46ae1f696db48b54ef71f90b05457 Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Wed, 14 Feb 2024 13:10:59 +0000 Subject: [PATCH 118/216] Change domain file for class --- src/controllers/post.js | 18 +-- src/domain/post.js | 225 +++++++++++++++++++------------- src/middleware/commentErrors.js | 4 +- src/server.js | 1 + 4 files changed, 143 insertions(+), 105 deletions(-) diff --git a/src/controllers/post.js b/src/controllers/post.js index ba1503e0..7d93c82e 100644 --- a/src/controllers/post.js +++ b/src/controllers/post.js @@ -1,10 +1,4 @@ -import { - createPost, - getPosts, - deletePostByIdAndUserId, - updatePostByIdAndUserId, - toggleLike -} from '../domain/post.js' +import Post from '../domain/post.js' import { sendDataResponse } from '../utils/responses.js' export const create = async (req, res) => { @@ -16,7 +10,7 @@ export const create = async (req, res) => { } try { - const post = await createPost(content, userId) + const post = await Post.create(content, userId) return sendDataResponse(res, 201, post) } catch (e) { console.error('error creating post', e.message) @@ -25,7 +19,7 @@ export const create = async (req, res) => { } export const getAll = async (req, res) => { - const posts = await getPosts() + const posts = await Post.getAll() return sendDataResponse(res, 200, { posts }) } @@ -34,7 +28,7 @@ export const deletePost = async (req, res) => { const userId = req.user.id try { - const result = await deletePostByIdAndUserId(postId, userId) + const result = await Post.deleteByIdAndUserId(postId, userId) if (result && result.error) { return sendDataResponse(res, result.status, { error: result.error }) } else { @@ -59,7 +53,7 @@ export const editPost = async (req, res) => { } try { - const result = await updatePostByIdAndUserId(postId, userId, content) + const result = await Post.updateByIdAndUserId(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 }) @@ -80,7 +74,7 @@ export const likePost = async (req, res) => { const userId = req.user.id try { - const message = await toggleLike(Number(postId), userId) + const message = await Post.toggleLike(Number(postId), userId) res.status(200).json({ message }) } catch (error) { console.error('Error handling like action:', error) diff --git a/src/domain/post.js b/src/domain/post.js index 16d62138..32db5a3d 100644 --- a/src/domain/post.js +++ b/src/domain/post.js @@ -1,118 +1,161 @@ 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 -} +export default class Post { + constructor(id, content, userId, likes, author, createdAt, updatedAt) { + this.id = id + this.content = content + this.userId = userId + this.likes = likes + this.author = author + this.createdAt = createdAt + this.updatedAt = updatedAt + } -export async function getPosts() { - const posts = await dbClient.post.findMany({ - include: { - user: { - include: { - profile: true + static async create(content, userId) { + const createdPost = await dbClient.post.create({ + data: { + content, + user: { + connect: { + id: userId + } } }, - likes: true - } - }) + include: { + user: true + } + }) + + return new Post( + createdPost.id, + createdPost.content, + createdPost.user.id, + createdPost.likes, + { + firstName: createdPost.user.profile.firstName, + lastName: createdPost.user.profile.lastName + }, + createdPost.createdAt, + createdPost.updatedAt + ) + } - 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}` + static async getAll() { + const posts = await dbClient.post.findMany({ + include: { + user: { + include: { + profile: true + } + }, + likes: true + } + }) + + 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}` + ) + } + + return new Post( + post.id, + post.content, + post.user.id, + post.likes, + { + firstName: profile.firstName, + lastName: profile.lastName + }, + post.createdAt, + post.updatedAt ) + }) + } + + static async deleteByIdAndUserId(postId, userId) { + const post = await dbClient.post.findUnique({ where: { id: postId } }) + + if (!post) { + return { error: 'Post not found', status: 404 } } - return { - id: post.id, - content: post.content, - createdAt: post.createdAt, - updatedAt: post.updatedAt, - userId: post.user.id, - likes: post.likes, - author: { - firstName: profile.firstName, - lastName: profile.lastName + if (post.userId !== userId) { + return { + error: 'You are not authorized to delete this post', + status: 403 } } - }) -} -export async function deletePostByIdAndUserId(postId, userId) { - const post = await dbClient.post.findUnique({ where: { id: postId } }) - - if (!post) { - return { error: 'Post not found', status: 404 } + await dbClient.post.delete({ where: { id: postId } }) + return { message: 'Post deleted successfully' } } - if (post.userId !== userId) { - return { error: 'You are not authorized to delete this post', status: 403 } - } + static async updateByIdAndUserId(postId, userId, content) { + const post = await dbClient.post.findUnique({ where: { id: postId } }) - await dbClient.post.delete({ where: { id: postId } }) - return { message: 'Post deleted successfully' } -} + if (!post) { + return { error: 'Post not found', status: 404 } + } -export async function updatePostByIdAndUserId(postId, userId, content) { - const post = await dbClient.post.findUnique({ where: { id: postId } }) + if (post.userId !== userId) { + return { + error: 'You are not authorized to update this post', + status: 403 + } + } - if (!post) { - return { error: 'Post not found', status: 404 } - } + const updatedPost = await dbClient.post.update({ + where: { id: postId }, + data: { content } + }) - if (post.userId !== userId) { - return { error: 'You are not authorized to update this post', status: 403 } + return { + post: new Post( + updatedPost.id, + updatedPost.content, + updatedPost.userId, + updatedPost.likes, + { + firstName: updatedPost.user.profile.firstName, + lastName: updatedPost.user.profile.lastName + }, + updatedPost.createdAt, + updatedPost.updatedAt + ) + } } - const updatedPost = await dbClient.post.update({ - where: { id: postId }, - data: { content } - }) - - return { post: updatedPost } -} + static async toggleLike(postId, userId) { + const existingLike = await dbClient.like.findFirst({ + where: { + AND: [{ postId: postId }, { userId: userId }] + } + }) -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.' } - }) - if (existingLike) { - await dbClient.like.delete({ where: { id: existingLike.id } }) - return 'Like removed successfully.' + await dbClient.like.create({ + data: { + postId, + userId + } + }) + return 'Like added successfully.' } - await dbClient.like.create({ - data: { - postId, - userId - } - }) - return 'Like added successfully.' -} - -export const getPostByIdDb = async (postId) => { - const foundPost = await dbClient.post.findFirst({ - where: { - id: Number(postId) - } - }) + static async getById(postId) { + const foundPost = await dbClient.post.findFirst({ + where: { + id: Number(postId) + } + }) - return foundPost + return foundPost + } } diff --git a/src/middleware/commentErrors.js b/src/middleware/commentErrors.js index f5341464..ca6ed72a 100644 --- a/src/middleware/commentErrors.js +++ b/src/middleware/commentErrors.js @@ -1,4 +1,4 @@ -import { getPostByIdDb } from '../domain/post.js' +import Post from '../domain/post.js' const errorCreator = (message, status) => { const error = new Error(message) @@ -24,7 +24,7 @@ export const checkPostExist = async (req, res, next) => { const { postId } = req.params try { - const foundPost = await getPostByIdDb(postId) + const foundPost = await Post.getById(postId) if (!foundPost) { throw errorCreator(`Post with provided id ${postId} does not exist`, 404) diff --git a/src/server.js b/src/server.js index 76855e64..61448b75 100644 --- a/src/server.js +++ b/src/server.js @@ -32,6 +32,7 @@ app.use('/', authRouter) app.use('/comments', commentRouter) app.use((err, req, res, next) => { + console.log(err.message) res.status(err.status ?? 500).json({ status: 'error', data: { From d126bf3144cf56369252a0aaad3c72c4b8fdc1c6 Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Wed, 14 Feb 2024 13:12:42 +0000 Subject: [PATCH 119/216] emove all comments --- src/server.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/server.js b/src/server.js index 61448b75..76855e64 100644 --- a/src/server.js +++ b/src/server.js @@ -32,7 +32,6 @@ app.use('/', authRouter) app.use('/comments', commentRouter) app.use((err, req, res, next) => { - console.log(err.message) res.status(err.status ?? 500).json({ status: 'error', data: { From 963b0abe4d36dc11c68169bb21b2c422fdced262 Mon Sep 17 00:00:00 2001 From: Satoki Date: Wed, 14 Feb 2024 15:44:08 +0000 Subject: [PATCH 120/216] updated status code for incorrect login details --- src/controllers/auth.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/auth.js b/src/controllers/auth.js index a1dfb734..d04007f1 100644 --- a/src/controllers/auth.js +++ b/src/controllers/auth.js @@ -18,7 +18,7 @@ export const login = async (req, res) => { const areCredentialsValid = await validateCredentials(password, foundUser) if (!areCredentialsValid) { - return sendDataResponse(res, 400, { + return sendDataResponse(res, 401, { error: 'Invalid email and/or password provided' }) } From 9887ec1b89609b0492cf43d203640dcca62bc1d8 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Wed, 14 Feb 2024 16:44:29 +0000 Subject: [PATCH 121/216] build: add department model to schema --- .../migration.sql | 33 +++++++++++++++++++ prisma/schema.prisma | 30 ++++++++++++----- 2 files changed, 54 insertions(+), 9 deletions(-) create mode 100644 prisma/migrations/20240214154916_add_department_model/migration.sql diff --git a/prisma/migrations/20240214154916_add_department_model/migration.sql b/prisma/migrations/20240214154916_add_department_model/migration.sql new file mode 100644 index 00000000..5aeb9a9d --- /dev/null +++ b/prisma/migrations/20240214154916_add_department_model/migration.sql @@ -0,0 +1,33 @@ +/* + Warnings: + + - You are about to drop the column `department` on the `Teacher` table. All the data in the column will be lost. + - Added the required column `departmentId` to the `Cohort` table without a default value. This is not possible if the table is not empty. + - Added the required column `departmentId` to the `Teacher` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "Cohort" ADD COLUMN "departmentId" INTEGER NOT NULL; + +-- AlterTable +ALTER TABLE "Teacher" DROP COLUMN "department", +ADD COLUMN "departmentId" INTEGER NOT NULL; + +-- CreateTable +CREATE TABLE "Department" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Department_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "Department_name_key" ON "Department"("name"); + +-- AddForeignKey +ALTER TABLE "Teacher" ADD CONSTRAINT "Teacher_departmentId_fkey" FOREIGN KEY ("departmentId") REFERENCES "Department"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Cohort" ADD CONSTRAINT "Cohort_departmentId_fkey" FOREIGN KEY ("departmentId") REFERENCES "Department"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index cc135a77..76b767f4 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -46,19 +46,31 @@ model Profile { } model Teacher { - id Int @id @default(autoincrement()) - userId Int @unique - user User @relation(fields: [userId], references: [id]) - department String - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + id Int @id @default(autoincrement()) + userId Int @unique + user User @relation(fields: [userId], references: [id]) + departmentId Int + department Department @relation(fields: [departmentId], references: [id]) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt } model Cohort { + id Int @id @default(autoincrement()) + name String + users User[] + departmentId Int + department Department @relation(fields: [departmentId], references: [id]) + deliveryLogs DeliveryLog[] + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model Department { id Int @id @default(autoincrement()) - name String - users User[] - deliveryLogs DeliveryLog[] + name String @unique + teachers Teacher[] + cohorts Cohort[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } From bccf78cf1f328363f76817b18efddff19d66ed19 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Wed, 14 Feb 2024 16:45:07 +0000 Subject: [PATCH 122/216] build: update seed to account for departments --- prisma/seed.js | 88 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 72 insertions(+), 16 deletions(-) diff --git a/prisma/seed.js b/prisma/seed.js index 14f79847..1e5b3fa6 100644 --- a/prisma/seed.js +++ b/prisma/seed.js @@ -3,33 +3,57 @@ import bcrypt from 'bcrypt' const prisma = new PrismaClient() async function seed() { - const cohort = await createCohort() + const department1 = await createDepartment('Software Development') + const department2 = await createDepartment('Data Analytics') + const cohort1 = await createCohort('Cohort 1', department1) + const cohort2 = await createCohort('Cohort 2', department2) + await createCohort('Cohort 3', department1) - const student = await createUserWithRole( + const student1 = await createUserWithRole( 'student@test.com', 'Testpassword1!', 'STUDENT', - cohort.id, + cohort1.id, 'Joe', 'Bloggs', 'Hello, world!', 'https://github.com/student1' ) - console.log(cohort.id) + await createUserWithRole( + 'student2@test.com', + 'Testpassword1!', + 'STUDENT', + cohort2.id, + 'Lee', + 'Dev', + 'Hello, world!', + 'https://github.com/student1' + ) // Creating teacher users with specific departments - const teacher = await createUserWithRole( - 'rick@test.com', - 'Testpassword2!', + const teacher1 = await createUserWithRole( + 'teacher@test.com', + 'Testpassword1!', 'TEACHER', null, 'Rick', 'Sanchez', 'Wubba Lubba Dub Dub!', 'https://github.com/rick', - 'Software Developer' + department1 + ) + await createUserWithRole( + 'teacher2@test.com', + 'Testpassword1!', + 'TEACHER', + null, + 'Max', + 'Sminth', + 'Hello there', + 'https://github.com/max', + department2 ) await createPost( - student.id, + student1.id, 'My first post!', [ { content: 'hi', userId: 2 }, @@ -37,7 +61,7 @@ async function seed() { ], [{ userId: 2 }] ) - await createPost(teacher.id, 'Hello, students', [], [{ userId: 1 }]) + await createPost(teacher1.id, 'Hello, students', [], [{ userId: 1 }]) process.exit(0) } @@ -66,13 +90,29 @@ async function createPost(userId, content, comments, likes) { return post } -async function createCohort() { +async function createDepartment(name) { + const department = await prisma.department.create({ + data: { + name + } + }) + console.info('Department created', department) + return department +} + +async function createCohort(name, department) { const cohort = await prisma.cohort.create({ data: { - name: 'Cohort 4' + name, + department: { + connect: { + id: department.id + } + } }, include: { - users: true + users: true, + department: true } }) @@ -90,7 +130,7 @@ async function createUserWithRole( lastName, bio, githubUrl, - department = null + department ) { const hashedPassword = await bcrypt.hash(password, 10) const userData = { @@ -118,8 +158,23 @@ async function createUserWithRole( if (role === 'TEACHER' && department) { await prisma.teacher.create({ data: { - userId: user.id, - department + user: { + connect: { + id: user.id + } + }, + department: { + connect: { + id: department.id + } + } + }, + include: { + department: { + select: { + name: true + } + } } }) } @@ -127,6 +182,7 @@ async function createUserWithRole( console.info(`${role} created:`, user.email) return user } + seed().catch(async (e) => { console.error(e) await prisma.$disconnect() From ac5af7378d0ed6054455947c147abd34d2ed9fdb Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Wed, 14 Feb 2024 16:45:50 +0000 Subject: [PATCH 123/216] feat: update GET/cohorts routes to include department name --- src/domain/cohort.js | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/domain/cohort.js b/src/domain/cohort.js index 22547d53..c0eff51a 100644 --- a/src/domain/cohort.js +++ b/src/domain/cohort.js @@ -13,10 +13,16 @@ export async function createCohort() { } export class Cohort { - constructor(id = null, name = 'default name', users = []) { + constructor( + id = null, + name = 'default name', + users = [], + departmentId = null + ) { this.id = id this.name = name this.users = users + this.departmentId = departmentId } static fromDb(cohort) { @@ -24,7 +30,19 @@ export class Cohort { } static async _findMany() { - return dbClient.cohort.findMany() + if (!this.departmentId) { + return dbClient.cohort.findMany() + } + + return dbClient.cohort.findMany({ + include: { + department: { + select: { + name: true + } + } + } + }) } static async getAll() { From f8833b0f2fe3ef27e1705ecef779abc369b428f9 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Wed, 14 Feb 2024 16:46:20 +0000 Subject: [PATCH 124/216] feat: update api docs to reflect changes --- docs/openapi.yml | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/docs/openapi.yml b/docs/openapi.yml index 2f1da813..8ddf4f0d 100644 --- a/docs/openapi.yml +++ b/docs/openapi.yml @@ -427,6 +427,11 @@ components: name: type: string format: string + department: + type: object + properties: + name: + type: string createdAt: type: string format: string @@ -441,23 +446,7 @@ components: type: string data: type: object - properties: - cohorts: - type: array - items: - type: object - properties: - id: - type: integer - name: - type: string - format: string - createdAt: - type: string - format: string - updatedAt: - type: string - format: string + $ref: '#/components/schemas/Cohort' AllUsers: type: object From a5ec0c28359ae70c0e56e8bcc21e0a8761665e34 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Wed, 14 Feb 2024 16:49:00 +0000 Subject: [PATCH 125/216] chore: remove redundant check --- src/domain/cohort.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/domain/cohort.js b/src/domain/cohort.js index c0eff51a..8499a071 100644 --- a/src/domain/cohort.js +++ b/src/domain/cohort.js @@ -30,10 +30,6 @@ export class Cohort { } static async _findMany() { - if (!this.departmentId) { - return dbClient.cohort.findMany() - } - return dbClient.cohort.findMany({ include: { department: { From 66d85f197cbe36b4017483340c1a5af40aae83bd Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Wed, 14 Feb 2024 17:06:45 +0000 Subject: [PATCH 126/216] feat: add default department name --- src/domain/cohort.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/domain/cohort.js b/src/domain/cohort.js index 8499a071..e2fe2db9 100644 --- a/src/domain/cohort.js +++ b/src/domain/cohort.js @@ -26,6 +26,9 @@ export class Cohort { } static fromDb(cohort) { + if (!cohort.department || !cohort.department.name) { + this.department.name = 'default department name' + } return new Cohort(cohort) } From 5c99e91998585eab7722104322ba7713e6361d43 Mon Sep 17 00:00:00 2001 From: Satoki Date: Wed, 14 Feb 2024 17:08:09 +0000 Subject: [PATCH 127/216] add prisma error check in create user funct --- src/controllers/user.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/controllers/user.js b/src/controllers/user.js index 7d5f07f6..616b9ef0 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -1,5 +1,6 @@ import User from '../domain/user.js' import { sendDataResponse, sendMessageResponse } from '../utils/responses.js' +import { PrismaClientKnownRequestError } from '@prisma/client/runtime/index.js' export const create = async (req, res) => { const userToCreate = await User.fromJson(req.body) @@ -15,6 +16,11 @@ export const create = async (req, res) => { return sendDataResponse(res, 201, createdUser) } catch (error) { + if (error instanceof PrismaClientKnownRequestError) { + if (error.code === 'P2002') { + return sendDataResponse(res, 409, { error: 'Email already in use' }) + } + } console.error('Error creating user', error) return sendMessageResponse(res, 500, 'Unable to create new user') } From 2c5ec7c834a18f3154c556067ccd27ee28edb103 Mon Sep 17 00:00:00 2001 From: ilham saleh Date: Thu, 15 Feb 2024 12:08:33 +0000 Subject: [PATCH 128/216] checking the errosr --- src/controllers/auth.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/controllers/auth.js b/src/controllers/auth.js index d04007f1..b8da5be7 100644 --- a/src/controllers/auth.js +++ b/src/controllers/auth.js @@ -27,6 +27,7 @@ export const login = async (req, res) => { return sendDataResponse(res, 200, { token, ...foundUser.toJSON() }) } catch (e) { + console.log('Error Login:', e) return sendMessageResponse(res, 500, 'Unable to process request') } } From 6c752e5cb99dbb30643a1c6dc4ab4afb6e84a600 Mon Sep 17 00:00:00 2001 From: LAVINIABENZAR Date: Thu, 15 Feb 2024 08:35:13 -0800 Subject: [PATCH 129/216] student model added --- .../migration.sql | 32 +++++++++++++++ prisma/schema.prisma | 16 ++++++-- prisma/seed.js | 39 ++++++++++++++++--- 3 files changed, 78 insertions(+), 9 deletions(-) create mode 100644 prisma/migrations/20240215152246_student_model/migration.sql diff --git a/prisma/migrations/20240215152246_student_model/migration.sql b/prisma/migrations/20240215152246_student_model/migration.sql new file mode 100644 index 00000000..ffed63c1 --- /dev/null +++ b/prisma/migrations/20240215152246_student_model/migration.sql @@ -0,0 +1,32 @@ +/* + Warnings: + + - You are about to drop the column `cohortId` on the `User` table. All the data in the column will be lost. + +*/ +-- DropForeignKey +ALTER TABLE "User" DROP CONSTRAINT "User_cohortId_fkey"; + +-- AlterTable +ALTER TABLE "User" DROP COLUMN "cohortId"; + +-- CreateTable +CREATE TABLE "Student" ( + "id" SERIAL NOT NULL, + "title" TEXT NOT NULL, + "userId" INTEGER NOT NULL, + "cohortId" INTEGER, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Student_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "Student_userId_key" ON "Student"("userId"); + +-- AddForeignKey +ALTER TABLE "Student" ADD CONSTRAINT "Student_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Student" ADD CONSTRAINT "Student_cohortId_fkey" FOREIGN KEY ("cohortId") REFERENCES "Cohort"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 76b767f4..5d81d693 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -22,8 +22,6 @@ model User { password String role Role @default(STUDENT) profile Profile? - cohortId Int? - cohort Cohort? @relation(fields: [cohortId], references: [id]) posts Post[] comments Comment[] likes Like[] @@ -31,6 +29,18 @@ model User { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt Teacher Teacher? + Student Student? +} + +model Student { + id Int @id @default(autoincrement()) + title String + userId Int @unique + user User @relation(fields: [userId], references: [id]) + cohortId Int? + cohort Cohort? @relation(fields: [cohortId], references: [id]) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt } model Profile { @@ -58,7 +68,7 @@ model Teacher { model Cohort { id Int @id @default(autoincrement()) name String - users User[] + students Student[] departmentId Int department Department @relation(fields: [departmentId], references: [id]) deliveryLogs DeliveryLog[] diff --git a/prisma/seed.js b/prisma/seed.js index 1e5b3fa6..b65777c0 100644 --- a/prisma/seed.js +++ b/prisma/seed.js @@ -17,7 +17,8 @@ async function seed() { 'Joe', 'Bloggs', 'Hello, world!', - 'https://github.com/student1' + 'https://github.com/student1', + 'Software Developer', ) await createUserWithRole( 'student2@test.com', @@ -27,7 +28,8 @@ async function seed() { 'Lee', 'Dev', 'Hello, world!', - 'https://github.com/student1' + 'https://github.com/student1', + 'Data Analyst', ) // Creating teacher users with specific departments const teacher1 = await createUserWithRole( @@ -47,7 +49,7 @@ async function seed() { 'TEACHER', null, 'Max', - 'Sminth', + 'Smith', 'Hello there', 'https://github.com/max', department2 @@ -111,7 +113,7 @@ async function createCohort(name, department) { } }, include: { - users: true, + students: true, department: true } }) @@ -130,14 +132,14 @@ async function createUserWithRole( lastName, bio, githubUrl, - department + title, + department, ) { const hashedPassword = await bcrypt.hash(password, 10) const userData = { email, password: hashedPassword, role, - cohortId, profile: { create: { firstName, @@ -179,6 +181,31 @@ async function createUserWithRole( }) } + if (role === 'STUDENT') { + await prisma.student.create({ + data: { + user: { + connect: { + id: user.id + } + }, + cohort: { + connect:{ + id: cohortId + } + }, + title + }, + include: { + cohort: { + select: { + name: true + } + } + } + }) + } + console.info(`${role} created:`, user.email) return user } From d7ccfa4b05ba0bc9e0251af27c405cb2997abdd4 Mon Sep 17 00:00:00 2001 From: ilham saleh Date: Thu, 15 Feb 2024 17:58:44 +0000 Subject: [PATCH 130/216] Profile prisma schema model modified --- .../20240215164032_add/migration.sql | 2 ++ prisma/schema.prisma | 1 + src/domain/user.js | 21 ++++++++++++++++--- 3 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 prisma/migrations/20240215164032_add/migration.sql diff --git a/prisma/migrations/20240215164032_add/migration.sql b/prisma/migrations/20240215164032_add/migration.sql new file mode 100644 index 00000000..6d58e6ff --- /dev/null +++ b/prisma/migrations/20240215164032_add/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Profile" ADD COLUMN "imageUrl" TEXT; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 76b767f4..93db2122 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -41,6 +41,7 @@ model Profile { lastName String bio String? githubUrl String? + imageUrl String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } diff --git a/src/domain/user.js b/src/domain/user.js index 39985944..db973787 100644 --- a/src/domain/user.js +++ b/src/domain/user.js @@ -19,6 +19,7 @@ export default class User { user.email, user.profile?.bio, user.profile?.githubUrl, + user.profile?.imageUrl, user.password, user.role ) @@ -26,7 +27,15 @@ export default class User { static async fromJson(json) { // eslint-disable-next-line camelcase - const { firstName, lastName, email, biography, githubUrl, password } = json + const { + firstName, + lastName, + email, + biography, + githubUrl, + password, + imageUrl + } = json const passwordHash = await bcrypt.hash(password, 8) @@ -38,6 +47,7 @@ export default class User { email, biography, githubUrl, + imageUrl, passwordHash ) } @@ -50,6 +60,7 @@ export default class User { email, bio, githubUrl, + imageUrl, passwordHash = null, role = 'STUDENT' ) { @@ -60,6 +71,7 @@ export default class User { this.email = email this.bio = bio this.githubUrl = githubUrl + this.imageUrl = imageUrl this.passwordHash = passwordHash this.role = role } @@ -73,8 +85,10 @@ export default class User { firstName: this.firstName, lastName: this.lastName, email: this.email, + password: this.passwordHash, biography: this.bio, - githubUrl: this.githubUrl + githubUrl: this.githubUrl, + imageUrl: this.imageUrl } } } @@ -103,7 +117,8 @@ export default class User { firstName: this.firstName, lastName: this.lastName, bio: this.bio, - githubUrl: this.githubUrl + githubUrl: this.githubUrl, + imageUrl: this.imageUrl } } From 3197d225344a6bd15d857c21d0379eba4e30dc99 Mon Sep 17 00:00:00 2001 From: ilham saleh Date: Thu, 15 Feb 2024 18:28:14 +0000 Subject: [PATCH 131/216] Avoid returning user password to front end --- src/domain/user.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/domain/user.js b/src/domain/user.js index db973787..ecbd9c23 100644 --- a/src/domain/user.js +++ b/src/domain/user.js @@ -85,7 +85,6 @@ export default class User { firstName: this.firstName, lastName: this.lastName, email: this.email, - password: this.passwordHash, biography: this.bio, githubUrl: this.githubUrl, imageUrl: this.imageUrl From 79844cbf0fc0f4734308c58675e22ccfdc7bacff Mon Sep 17 00:00:00 2001 From: ilham saleh Date: Fri, 16 Feb 2024 11:16:55 +0000 Subject: [PATCH 132/216] update apidocs --- docs/openapi.yml | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/docs/openapi.yml b/docs/openapi.yml index 40780a96..d2e0a407 100644 --- a/docs/openapi.yml +++ b/docs/openapi.yml @@ -359,7 +359,7 @@ paths: application/json: schema: $ref: '#/components/schemas/Cohorts' - + /comments: get: tags: @@ -443,11 +443,11 @@ components: type: integer name: type: string - format: string - department: + format: string + department: type: object properties: - name: + name: type: string createdAt: type: string @@ -455,15 +455,15 @@ components: updatedAt: type: string format: string - + Cohorts: type: object - properties: - status: + properties: + status: type: string - data: + data: type: object - $ref: '#/components/schemas/Cohort' + $ref: '#/components/schemas/Cohort' AllUsers: type: object @@ -497,6 +497,8 @@ components: type: string githubUrl: type: string + imageUrl: + type: string CreateUser: type: object @@ -513,6 +515,8 @@ components: type: string password: type: string + imageUrl: + type: string UpdateUser: type: object @@ -533,6 +537,8 @@ components: type: string githubUrl: type: string + imageUrl: + type: string Posts: type: object From 27eee81dc74f6e351a2249eff8a1dc8950b10029 Mon Sep 17 00:00:00 2001 From: Kye Date: Fri, 16 Feb 2024 12:41:01 +0000 Subject: [PATCH 133/216] add new route to /cohorts --- src/routes/cohort.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/routes/cohort.js b/src/routes/cohort.js index 1c1b23c2..c076d4f3 100644 --- a/src/routes/cohort.js +++ b/src/routes/cohort.js @@ -1,5 +1,9 @@ import { Router } from 'express' -import { create, getCohorts } from '../controllers/cohort.js' +import { + create, + getCohorts, + getStudentsByCohortId +} from '../controllers/cohort.js' import { validateAuthentication, validateTeacherRole @@ -9,5 +13,6 @@ const router = Router() router.post('/', validateAuthentication, validateTeacherRole, create) router.get('/', validateAuthentication, validateTeacherRole, getCohorts) +router.get('/:id/students', validateAuthentication, getStudentsByCohortId) export default router From 885b6e5f099ac2864d01749d4caa02272664bc69 Mon Sep 17 00:00:00 2001 From: Kye Date: Fri, 16 Feb 2024 12:41:52 +0000 Subject: [PATCH 134/216] add getStudentsByCohortId controller --- src/controllers/cohort.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/controllers/cohort.js b/src/controllers/cohort.js index cb678b12..e732ae29 100644 --- a/src/controllers/cohort.js +++ b/src/controllers/cohort.js @@ -1,4 +1,5 @@ import { createCohort, Cohort } from '../domain/cohort.js' +import { Student } from '../domain/student.js' import { sendDataResponse, sendMessageResponse } from '../utils/responses.js' export const create = async (req, res) => { @@ -20,3 +21,28 @@ export const getCohorts = async (req, res) => { return sendMessageResponse(res, 500, 'Unable to get the list of cohorts') } } + +export const getStudentsByCohortId = async (req, res) => { + const cohortId = parseInt(req.params.id) + + if (!cohortId) { + return sendMessageResponse(res, 400, { error: 'No provided cohort ID' }) + } + + try { + const foundCohort = await Cohort.getAll() + + const hasCohort = foundCohort.find((cohort) => cohort.id.id === cohortId) + + if (!hasCohort) { + return sendMessageResponse(res, 404, 'No cohort found with provided ID') + } + + const foundStudents = await Student.getAllStudentsByCohortId(cohortId) + + return res.status(200).send({ students: foundStudents }) + } catch (e) { + console.log(e) + return sendMessageResponse(res, 500, 'Unable to get students') + } +} From e80971ca0e7f808cd30309abcf87ec55ba0560c9 Mon Sep 17 00:00:00 2001 From: Kye Date: Fri, 16 Feb 2024 12:42:20 +0000 Subject: [PATCH 135/216] add Student class --- src/domain/student.js | 79 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 src/domain/student.js diff --git a/src/domain/student.js b/src/domain/student.js new file mode 100644 index 00000000..ea02a811 --- /dev/null +++ b/src/domain/student.js @@ -0,0 +1,79 @@ +import dbClient from '../utils/dbClient.js' + +export class Student { + constructor( + id = null, + title = null, + user = null, + userId = null, + cohortId = null + ) { + this.id = id + this.title = title + this.user = user + this.userId = userId + this.cohortId = cohortId + } + + static fromDb(student) { + return new Student( + student.id, + student.title, + student.user, + student.userId, + student.cohortId + ) + } + + static async _findMany() { + return dbClient.student.findMany({ + include: { + cohort: true, + user: { + profile: true + } + } + }) + } + + static async _findManyWhere(key, value) { + return dbClient.student.findMany({ + where: { + [key]: value + }, + include: { + user: { + include: { + profile: true + } + } + } + }) + } + + static async getAll() { + const foundStudents = await Student._findMany() + + const allStudents = foundStudents.map(Student.fromDb) + return allStudents + } + + static async getAllStudentsByCohortId(id) { + const foundStudents = await Student._findManyWhere('cohortId', id) + + const allStudents = foundStudents.map(Student.fromDb) + return allStudents + } + + toJSON() { + return { + id: this.id, + title: this.title, + email: this.user.email, + firstName: this.user.profile.firstName, + lastName: this.user.profile.lastName, + userId: this.userId, + cohortId: this.cohortId + } + } +} From b8e1d31870f95bd33d60ec0796ac4221a48fc20b Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Fri, 16 Feb 2024 12:50:27 +0000 Subject: [PATCH 136/216] fix: fromDb and fromJSON return correct data structure --- src/domain/cohort.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/domain/cohort.js b/src/domain/cohort.js index e2fe2db9..3a852eed 100644 --- a/src/domain/cohort.js +++ b/src/domain/cohort.js @@ -29,7 +29,13 @@ export class Cohort { if (!cohort.department || !cohort.department.name) { this.department.name = 'default department name' } - return new Cohort(cohort) + const newCohort = new Cohort( + cohort.id, + cohort.name, + cohort.users, + cohort.departmentId + ) + return newCohort } static async _findMany() { @@ -53,7 +59,10 @@ export class Cohort { toJSON() { return { cohort: { - id: this.id + id: this.id, + name: this.name, + users: this.users, + departmentId: this.departmentId } } } From e3370d3d501bbcb3c4667c7ff44ffa54933a961d Mon Sep 17 00:00:00 2001 From: Kye Date: Fri, 16 Feb 2024 12:56:21 +0000 Subject: [PATCH 137/216] update console.log error message --- src/controllers/cohort.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/cohort.js b/src/controllers/cohort.js index e732ae29..ec6edce5 100644 --- a/src/controllers/cohort.js +++ b/src/controllers/cohort.js @@ -42,7 +42,7 @@ export const getStudentsByCohortId = async (req, res) => { return res.status(200).send({ students: foundStudents }) } catch (e) { - console.log(e) + console.log('Error getting cohort by ID:', e) return sendMessageResponse(res, 500, 'Unable to get students') } } From 5ae6b96d0e5f3b71e97cb8f969c617097250220f Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Fri, 16 Feb 2024 12:58:03 +0000 Subject: [PATCH 138/216] fix: add department property to cohort --- src/domain/cohort.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/domain/cohort.js b/src/domain/cohort.js index 3a852eed..270e8fa8 100644 --- a/src/domain/cohort.js +++ b/src/domain/cohort.js @@ -17,12 +17,14 @@ export class Cohort { id = null, name = 'default name', users = [], - departmentId = null + departmentId = null, + department = null ) { this.id = id this.name = name this.users = users this.departmentId = departmentId + this.department = department } static fromDb(cohort) { @@ -33,7 +35,8 @@ export class Cohort { cohort.id, cohort.name, cohort.users, - cohort.departmentId + cohort.departmentId, + cohort.department ) return newCohort } @@ -58,12 +61,11 @@ export class Cohort { toJSON() { return { - cohort: { - id: this.id, - name: this.name, - users: this.users, - departmentId: this.departmentId - } + id: this.id, + name: this.name, + users: this.users, + departmentId: this.departmentId, + department: this.department } } } From e34b98acaa617840339aafecdd996a08f78fa5bd Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Fri, 16 Feb 2024 13:10:49 +0000 Subject: [PATCH 139/216] docs: add missing property on cohorts --- docs/openapi.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/openapi.yml b/docs/openapi.yml index d2e0a407..70a864ef 100644 --- a/docs/openapi.yml +++ b/docs/openapi.yml @@ -444,6 +444,8 @@ components: name: type: string format: string + departmentId: + type: integer department: type: object properties: From b4e85d7919a4afff586e3f7afd3c3055a72f2251 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Fri, 16 Feb 2024 15:56:08 +0000 Subject: [PATCH 140/216] feat: JWT payload now includes user role --- src/controllers/auth.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/controllers/auth.js b/src/controllers/auth.js index b8da5be7..77494ac5 100644 --- a/src/controllers/auth.js +++ b/src/controllers/auth.js @@ -23,7 +23,7 @@ export const login = async (req, res) => { }) } - const token = generateJwt(foundUser.id) + const token = generateJwt(foundUser.id, foundUser.role) return sendDataResponse(res, 200, { token, ...foundUser.toJSON() }) } catch (e) { @@ -32,8 +32,8 @@ export const login = async (req, res) => { } } -function generateJwt(userId) { - return jwt.sign({ userId }, JWT_SECRET, { expiresIn: JWT_EXPIRY }) +function generateJwt(userId, userRole = 'STUDENT') { + return jwt.sign({ userId, userRole }, JWT_SECRET, { expiresIn: JWT_EXPIRY }) } async function validateCredentials(password, user) { From 20a844e925698df22a1cb63a62e50236937cc62f Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Sat, 17 Feb 2024 00:32:43 +0000 Subject: [PATCH 141/216] Add teachers route --- src/routes/teachers.js | 5 +++++ src/server.js | 2 ++ 2 files changed, 7 insertions(+) create mode 100644 src/routes/teachers.js diff --git a/src/routes/teachers.js b/src/routes/teachers.js new file mode 100644 index 00000000..93abe91d --- /dev/null +++ b/src/routes/teachers.js @@ -0,0 +1,5 @@ +import { Router } from 'express' + +const router = Router() + +export default router diff --git a/src/server.js b/src/server.js index 76855e64..1e79cc46 100644 --- a/src/server.js +++ b/src/server.js @@ -11,6 +11,7 @@ import authRouter from './routes/auth.js' import cohortRouter from './routes/cohort.js' import deliveryLogRouter from './routes/deliveryLog.js' import commentRouter from './routes/comment.js' +import teachersRoute from './routes/teachers.js' const app = express() app.disable('x-powered-by') @@ -30,6 +31,7 @@ app.use('/cohorts', cohortRouter) app.use('/logs', deliveryLogRouter) app.use('/', authRouter) app.use('/comments', commentRouter) +app.use('/teachers', teachersRoute) app.use((err, req, res, next) => { res.status(err.status ?? 500).json({ From 8fa5687cab197a00ee7b71e13463ca309a2bcbc2 Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Sat, 17 Feb 2024 00:34:12 +0000 Subject: [PATCH 142/216] Add get all teachers controller --- src/controllers/teachers.js | 1 + src/routes/teachers.js | 3 +++ 2 files changed, 4 insertions(+) create mode 100644 src/controllers/teachers.js diff --git a/src/controllers/teachers.js b/src/controllers/teachers.js new file mode 100644 index 00000000..069e3c3f --- /dev/null +++ b/src/controllers/teachers.js @@ -0,0 +1 @@ +export const getAllTeachers = async (req, res, next) => {} diff --git a/src/routes/teachers.js b/src/routes/teachers.js index 93abe91d..69b312e2 100644 --- a/src/routes/teachers.js +++ b/src/routes/teachers.js @@ -1,5 +1,8 @@ import { Router } from 'express' +import { getAllTeachers } from '../controllers/teachers.js' const router = Router() +router.get('/', getAllTeachers) + export default router From e67067f02f08bffbd8a975736f69d179d5ae758f Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Sat, 17 Feb 2024 00:43:24 +0000 Subject: [PATCH 143/216] Add domain teachers and new function get all teachers --- src/domain/teachers.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/domain/teachers.js diff --git a/src/domain/teachers.js b/src/domain/teachers.js new file mode 100644 index 00000000..ef4e0c9a --- /dev/null +++ b/src/domain/teachers.js @@ -0,0 +1,26 @@ +import dbClient from '../utils/dbClient.js' + +export class Teacher { + constructor(id = null, userId = null, departmentId = null) { + this.id = id + this.userId = userId + this.departmentId = departmentId + } + + static async _findMany() { + return dbClient.teacher.findMany({ + include: { + user: { + profile: true + }, + department: true + } + }) + } + + static async getAll() { + const foundTeachers = await Teacher._findMany() + + return foundTeachers + } +} From 52e05b849dc2795bae29590d072236f57d318bfd Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Sat, 17 Feb 2024 00:54:23 +0000 Subject: [PATCH 144/216] Add get all teachers controller --- src/controllers/teachers.js | 9 ++++++++- src/domain/teachers.js | 6 ++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/controllers/teachers.js b/src/controllers/teachers.js index 069e3c3f..455d1ebf 100644 --- a/src/controllers/teachers.js +++ b/src/controllers/teachers.js @@ -1 +1,8 @@ -export const getAllTeachers = async (req, res, next) => {} +import Teacher from '../domain/teachers.js' +import { sendDataResponse } from '../utils/responses.js' + +export const getAllTeachers = async (req, res) => { + const teachers = await Teacher.getAll() + + return sendDataResponse(res, 200, { teachers }) +} diff --git a/src/domain/teachers.js b/src/domain/teachers.js index ef4e0c9a..942e7cb6 100644 --- a/src/domain/teachers.js +++ b/src/domain/teachers.js @@ -1,6 +1,6 @@ import dbClient from '../utils/dbClient.js' -export class Teacher { +export default class Teacher { constructor(id = null, userId = null, departmentId = null) { this.id = id this.userId = userId @@ -11,7 +11,9 @@ export class Teacher { return dbClient.teacher.findMany({ include: { user: { - profile: true + include: { + profile: true + } }, department: true } From b0322e9f2492a18e3f0b523aa7837ac8a2f3dc3e Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Sat, 17 Feb 2024 01:04:55 +0000 Subject: [PATCH 145/216] Update seed file --- prisma/seed.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/prisma/seed.js b/prisma/seed.js index b65777c0..5cedf31a 100644 --- a/prisma/seed.js +++ b/prisma/seed.js @@ -18,7 +18,7 @@ async function seed() { 'Bloggs', 'Hello, world!', 'https://github.com/student1', - 'Software Developer', + 'Software Developer' ) await createUserWithRole( 'student2@test.com', @@ -29,7 +29,7 @@ async function seed() { 'Dev', 'Hello, world!', 'https://github.com/student1', - 'Data Analyst', + 'Data Analyst' ) // Creating teacher users with specific departments const teacher1 = await createUserWithRole( @@ -41,6 +41,7 @@ async function seed() { 'Sanchez', 'Wubba Lubba Dub Dub!', 'https://github.com/rick', + null, department1 ) await createUserWithRole( @@ -52,6 +53,7 @@ async function seed() { 'Smith', 'Hello there', 'https://github.com/max', + null, department2 ) await createPost( @@ -133,7 +135,7 @@ async function createUserWithRole( bio, githubUrl, title, - department, + department ) { const hashedPassword = await bcrypt.hash(password, 10) const userData = { @@ -190,7 +192,7 @@ async function createUserWithRole( } }, cohort: { - connect:{ + connect: { id: cohortId } }, From bc7b8d7788f5868a48b702e3b5c4aeefebcb20f0 Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Sat, 17 Feb 2024 01:06:08 +0000 Subject: [PATCH 146/216] Add validate token --- src/routes/teachers.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/routes/teachers.js b/src/routes/teachers.js index 69b312e2..5e84d7e8 100644 --- a/src/routes/teachers.js +++ b/src/routes/teachers.js @@ -1,8 +1,9 @@ import { Router } from 'express' import { getAllTeachers } from '../controllers/teachers.js' +import { validateAuthentication } from '../middleware/auth.js' const router = Router() -router.get('/', getAllTeachers) +router.get('/', validateAuthentication, getAllTeachers) export default router From a7dd5e81a575462d40aa6e29097b996e4ef19ca3 Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Sun, 18 Feb 2024 20:55:08 +0000 Subject: [PATCH 147/216] Update domain file of teacehrs --- src/domain/teachers.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/domain/teachers.js b/src/domain/teachers.js index 942e7cb6..7166ad33 100644 --- a/src/domain/teachers.js +++ b/src/domain/teachers.js @@ -1,12 +1,16 @@ import dbClient from '../utils/dbClient.js' export default class Teacher { - constructor(id = null, userId = null, departmentId = null) { + constructor(id = null, user = null, departmentId = null) { this.id = id - this.userId = userId + this.user = user this.departmentId = departmentId } + static fromDb(teacher) { + return new Teacher(teacher.id, teacher.user, teacher.departmentId) + } + static async _findMany() { return dbClient.teacher.findMany({ include: { @@ -23,6 +27,8 @@ export default class Teacher { static async getAll() { const foundTeachers = await Teacher._findMany() - return foundTeachers + const allTeachers = foundTeachers.map(Teacher.fromDb) + + return allTeachers } } From d427ce25207662884e81f5035bf154f324a7b05f Mon Sep 17 00:00:00 2001 From: Kye Date: Mon, 19 Feb 2024 11:53:38 +0000 Subject: [PATCH 148/216] update controller to match updated domain --- src/controllers/cohort.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/cohort.js b/src/controllers/cohort.js index ec6edce5..a646e8d2 100644 --- a/src/controllers/cohort.js +++ b/src/controllers/cohort.js @@ -32,7 +32,7 @@ export const getStudentsByCohortId = async (req, res) => { try { const foundCohort = await Cohort.getAll() - const hasCohort = foundCohort.find((cohort) => cohort.id.id === cohortId) + const hasCohort = foundCohort.find((cohort) => cohort.id === cohortId) if (!hasCohort) { return sendMessageResponse(res, 404, 'No cohort found with provided ID') From f8a2e39fcb2f55924d047d7df2630c98441a1657 Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Mon, 19 Feb 2024 13:51:27 +0000 Subject: [PATCH 149/216] Update docs file --- docs/openapi.yml | 56 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/docs/openapi.yml b/docs/openapi.yml index 70a864ef..3fe2ca35 100644 --- a/docs/openapi.yml +++ b/docs/openapi.yml @@ -65,6 +65,34 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' + /teachers: + get: + tags: + - teacher + summary: Get all teachers + description: '' + operationId: getAllTeachers + security: + - bearerAuth: [] + responses: + '200': + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/AllTeachers' + '400': + description: fail + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: fail + content: + application/json: + schema: + $ref: '#/components/schemas/Error' /login: post: tags: @@ -480,6 +508,34 @@ components: items: $ref: '#/components/schemas/User' + AllTeachers: + type: object + properties: + status: + type: string + data: + type: object + properties: + teachers: + type: array + items: + type: object + properties: + id: + type: integer + userId: + type: integer + departmentId: + type: integer + createdAt: + type: string + updatedAt: + type: string + user: + type: object + department: + type: object + User: type: object properties: From 3c036deb08830932ef5a6c6873b276194f94a519 Mon Sep 17 00:00:00 2001 From: Kye Date: Mon, 19 Feb 2024 16:35:00 +0000 Subject: [PATCH 150/216] update getSelf response to { data: req.user } --- 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 616b9ef0..66b52ea7 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -42,7 +42,7 @@ export const getById = async (req, res) => { } } -export const getSelf = async (req, res) => res.json({ self: req.user }) +export const getSelf = async (req, res) => res.json({ data: req.user }) export const getAll = async (req, res) => { const { name } = req.query From f13ea9bbed393124a89cb38e28763afcb7b0a0b7 Mon Sep 17 00:00:00 2001 From: LAVINIABENZAR Date: Mon, 19 Feb 2024 09:45:46 -0800 Subject: [PATCH 151/216] get endpoint for students --- src/controllers/student.js | 8 ++++++++ src/routes/student.js | 9 +++++++++ src/server.js | 2 ++ 3 files changed, 19 insertions(+) create mode 100644 src/controllers/student.js create mode 100644 src/routes/student.js diff --git a/src/controllers/student.js b/src/controllers/student.js new file mode 100644 index 00000000..5bc68538 --- /dev/null +++ b/src/controllers/student.js @@ -0,0 +1,8 @@ +import Student from '../domain/student.js' +import { sendDataResponse } from '../utils/responses.js' + +export const getAllStudents = async (req, res) => { + const students = await Student.getAll() + + return sendDataResponse(res, 200, { students }) +} \ No newline at end of file diff --git a/src/routes/student.js b/src/routes/student.js new file mode 100644 index 00000000..b340d2ba --- /dev/null +++ b/src/routes/student.js @@ -0,0 +1,9 @@ +import { Router } from "express"; +import { getAllStudents } from "../controllers/student"; +import { validateAuthentication } from "../middleware/auth"; + +const router = Router() + +router.get('/', validateAuthentication, getAllStudents) + +export default router \ No newline at end of file diff --git a/src/server.js b/src/server.js index 1e79cc46..17d47cc5 100644 --- a/src/server.js +++ b/src/server.js @@ -12,6 +12,7 @@ import cohortRouter from './routes/cohort.js' import deliveryLogRouter from './routes/deliveryLog.js' import commentRouter from './routes/comment.js' import teachersRoute from './routes/teachers.js' +import studentsRouter from './routes/student.js' const app = express() app.disable('x-powered-by') @@ -32,6 +33,7 @@ app.use('/logs', deliveryLogRouter) app.use('/', authRouter) app.use('/comments', commentRouter) app.use('/teachers', teachersRoute) +app.use('/students', studentsRouter) app.use((err, req, res, next) => { res.status(err.status ?? 500).json({ From 446fbe44a1b7731af12646dd9d49cf82fbb8daf4 Mon Sep 17 00:00:00 2001 From: LAVINIABENZAR Date: Mon, 19 Feb 2024 09:51:09 -0800 Subject: [PATCH 152/216] fix linting errors --- src/controllers/student.js | 2 +- src/routes/student.js | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/controllers/student.js b/src/controllers/student.js index 5bc68538..6c8ad96f 100644 --- a/src/controllers/student.js +++ b/src/controllers/student.js @@ -5,4 +5,4 @@ export const getAllStudents = async (req, res) => { const students = await Student.getAll() return sendDataResponse(res, 200, { students }) -} \ No newline at end of file +} diff --git a/src/routes/student.js b/src/routes/student.js index b340d2ba..cf61468c 100644 --- a/src/routes/student.js +++ b/src/routes/student.js @@ -1,9 +1,9 @@ -import { Router } from "express"; -import { getAllStudents } from "../controllers/student"; -import { validateAuthentication } from "../middleware/auth"; +import { Router } from 'express' +import { getAllStudents } from '../controllers/student' +import { validateAuthentication } from '../middleware/auth' const router = Router() router.get('/', validateAuthentication, getAllStudents) -export default router \ No newline at end of file +export default router From ce8d2fce89ddf231ddefba13a689b44cf022f22e Mon Sep 17 00:00:00 2001 From: LAVINIABENZAR Date: Mon, 19 Feb 2024 10:06:40 -0800 Subject: [PATCH 153/216] updated openapi.yml file --- docs/openapi.yml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/docs/openapi.yml b/docs/openapi.yml index 3fe2ca35..c57de001 100644 --- a/docs/openapi.yml +++ b/docs/openapi.yml @@ -93,6 +93,34 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' + /students: + get: + tags: + - student + summary: Get all students + description: '' + operationId: getAllStudents + security: + - bearerAuth: [] + responses: + '200': + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/AllStudents' + '400': + description: fail + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: fail + content: + application/json: + schema: + $ref: '#/components/schemas/Error' /login: post: tags: From c9fe75c872ef41a2e34cd5a7e4ab7a95eb5faeb1 Mon Sep 17 00:00:00 2001 From: LAVINIABENZAR Date: Mon, 19 Feb 2024 10:11:01 -0800 Subject: [PATCH 154/216] updated openapi.yml file --- docs/openapi.yml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/docs/openapi.yml b/docs/openapi.yml index c57de001..943b8d09 100644 --- a/docs/openapi.yml +++ b/docs/openapi.yml @@ -564,6 +564,34 @@ components: department: type: object + AllStudents: + type: object + properties: + status: + type: string + data: + type: object + properties: + students: + type: array + items: + type: object + properties: + id: + type: integer + userId: + type: integer + cohortId: + type: integer + createdAt: + type: string + updatedAt: + type: string + user: + type: object + title: + type: string + User: type: object properties: From 96bc80a1cd33aeba59c5c5ab89892227295be60b Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Mon, 19 Feb 2024 18:25:08 +0000 Subject: [PATCH 155/216] feat: add GET/teachers/:id route --- src/routes/teachers.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/routes/teachers.js b/src/routes/teachers.js index 5e84d7e8..72bf0b24 100644 --- a/src/routes/teachers.js +++ b/src/routes/teachers.js @@ -1,9 +1,10 @@ import { Router } from 'express' -import { getAllTeachers } from '../controllers/teachers.js' +import { getAllTeachers, getTeacher } from '../controllers/teachers.js' import { validateAuthentication } from '../middleware/auth.js' const router = Router() router.get('/', validateAuthentication, getAllTeachers) +router.get('/:id', validateAuthentication, getTeacher) export default router From f05bf52baf9052a821c82b4aedf1ae65d5439658 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Mon, 19 Feb 2024 18:26:09 +0000 Subject: [PATCH 156/216] feat: add getTeacher() controller --- src/controllers/teachers.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/controllers/teachers.js b/src/controllers/teachers.js index 455d1ebf..b9a309d4 100644 --- a/src/controllers/teachers.js +++ b/src/controllers/teachers.js @@ -6,3 +6,9 @@ export const getAllTeachers = async (req, res) => { return sendDataResponse(res, 200, { teachers }) } + +export const getTeacher = async (req, res) => { + const id = Number(req.params.id) + const teacher = await Teacher.getTeacherBy(id) + return sendDataResponse(res, 200, { teacher }) +} From 079778d1b25300563dd99e6c71f8adfb72f9a0fb Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Mon, 19 Feb 2024 18:27:30 +0000 Subject: [PATCH 157/216] feat: add getTeacherBy() and _findUnique() to class Teacher --- src/domain/teachers.js | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/domain/teachers.js b/src/domain/teachers.js index 7166ad33..1be86129 100644 --- a/src/domain/teachers.js +++ b/src/domain/teachers.js @@ -11,6 +11,22 @@ export default class Teacher { return new Teacher(teacher.id, teacher.user, teacher.departmentId) } + static async _findUnique(id) { + return dbClient.teacher.findUnique({ + where: { + id: id + }, + include: { + department: true, + user: { + include: { + profile: true + } + } + } + }) + } + static async _findMany() { return dbClient.teacher.findMany({ include: { @@ -24,9 +40,14 @@ export default class Teacher { }) } + static async getTeacherBy(teacherId) { + const teacher = await Teacher._findUnique(teacherId) + console.log(teacher) + return teacher + } + static async getAll() { const foundTeachers = await Teacher._findMany() - const allTeachers = foundTeachers.map(Teacher.fromDb) return allTeachers From fb49d3e2d77bee84ab01c46856bf501d40744e8c Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Mon, 19 Feb 2024 18:34:57 +0000 Subject: [PATCH 158/216] docs: add GET/teachers/:id route --- docs/openapi.yml | 52 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/docs/openapi.yml b/docs/openapi.yml index 3fe2ca35..5d4bd6db 100644 --- a/docs/openapi.yml +++ b/docs/openapi.yml @@ -93,6 +93,35 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' + + /teachers/:id: + get: + tags: + - teacher + summary: Get a teacher by id + description: '' + operationId: getTeacherById + security: + - bearerAuth: [] + responses: + '200': + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/Teacher' + '400': + description: fail + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: fail + content: + application/json: + schema: + $ref: '#/components/schemas/Error' /login: post: tags: @@ -536,6 +565,29 @@ components: department: type: object + Teacher: + type: object + properties: + status: + type: string + data: + type: object + properties: + id: + type: integer + userId: + type: integer + departmentId: + type: integer + createdAt: + type: string + updatedAt: + type: string + user: + $ref: '#/components/schemas/User' + department: + type: object + User: type: object properties: From ec7b9bae30c7613d28bf221d5efc1bdd769a7a90 Mon Sep 17 00:00:00 2001 From: LAVINIABENZAR Date: Tue, 20 Feb 2024 02:20:59 -0800 Subject: [PATCH 159/216] openapi.yml updated again --- docs/openapi.yml | 60 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/docs/openapi.yml b/docs/openapi.yml index 5d4bd6db..eaffc2ee 100644 --- a/docs/openapi.yml +++ b/docs/openapi.yml @@ -122,6 +122,36 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' + + /students: + get: + tags: + - students + summary: Get all students + description: '' + operationId: getAllStudents + security: + - bearerAuth: [] + responses: + '200': + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/AllStudents' + '400': + description: fail + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: fail + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /login: post: tags: @@ -563,7 +593,7 @@ components: user: type: object department: - type: object + type: object Teacher: type: object @@ -587,6 +617,34 @@ components: $ref: '#/components/schemas/User' department: type: object + + AllStudents: + type: object + properties: + status: + type: string + data: + type: object + properties: + students: + type: array + items: + type: object + properties: + id: + type: integer + userId: + type: integer + cohortId: + type: integer + createdAt: + type: string + updatedAt: + type: string + user: + type: object + title: + type: object User: type: object From 7a6fd479a004845dfa7cce018db73a5cf81d4ef9 Mon Sep 17 00:00:00 2001 From: callumhayden Date: Tue, 20 Feb 2024 10:41:50 +0000 Subject: [PATCH 160/216] created new router, controller and domain file that will allow the teacher to change the student's cohort --- src/controllers/cohort.js | 17 +++++++++++++++++ src/domain/student.js | 19 +++++++++++++++++++ src/routes/cohort.js | 9 ++++++++- 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/controllers/cohort.js b/src/controllers/cohort.js index ec6edce5..241eca03 100644 --- a/src/controllers/cohort.js +++ b/src/controllers/cohort.js @@ -46,3 +46,20 @@ export const getStudentsByCohortId = async (req, res) => { return sendMessageResponse(res, 500, 'Unable to get students') } } + +export const changeStudentCohort = async (req, res) => { + const { studentId, newCohortId } = req.body + + if (!studentId || !newCohortId) { + return sendMessageResponse(res, 400, 'Missing student ID or new cohort ID') + } + + try { + const changeResult = await Student.changeCohort(studentId, newCohortId) + console.log(changeResult) + return sendMessageResponse(res, 200, 'Student cohort changed successfully') + } catch (e) { + console.error('Error changing student cohort:', e) + return sendMessageResponse(res, 500, 'Internal server error') + } +} diff --git a/src/domain/student.js b/src/domain/student.js index ea02a811..acf2cbbb 100644 --- a/src/domain/student.js +++ b/src/domain/student.js @@ -65,6 +65,25 @@ export class Student { return allStudents } + static async changeCohort(studentId, newCohortId) { + const studentExists = await dbClient.student.findUnique({ + where: { id: studentId } + }) + if (!studentExists) throw new Error('Student not found') + + const cohortExists = await dbClient.cohort.findUnique({ + where: { id: newCohortId } + }) + if (!cohortExists) throw new Error('Cohort not found') + + const updatedStudent = await dbClient.student.update({ + where: { id: studentId }, + data: { cohortId: newCohortId } + }) + + return updatedStudent + } + toJSON() { return { id: this.id, diff --git a/src/routes/cohort.js b/src/routes/cohort.js index c076d4f3..9b61c2a3 100644 --- a/src/routes/cohort.js +++ b/src/routes/cohort.js @@ -2,7 +2,8 @@ import { Router } from 'express' import { create, getCohorts, - getStudentsByCohortId + getStudentsByCohortId, + changeStudentCohort } from '../controllers/cohort.js' import { validateAuthentication, @@ -14,5 +15,11 @@ const router = Router() router.post('/', validateAuthentication, validateTeacherRole, create) router.get('/', validateAuthentication, validateTeacherRole, getCohorts) router.get('/:id/students', validateAuthentication, getStudentsByCohortId) +router.put( + '/:id/students', + validateAuthentication, + validateTeacherRole, + changeStudentCohort +) export default router From 42b2060c5e62b76eed9213241e644f9c041b4b8b Mon Sep 17 00:00:00 2001 From: callumhayden Date: Tue, 20 Feb 2024 11:25:34 +0000 Subject: [PATCH 161/216] slight change to meet requirements --- src/controllers/cohort.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/controllers/cohort.js b/src/controllers/cohort.js index 241eca03..a1ce2e8e 100644 --- a/src/controllers/cohort.js +++ b/src/controllers/cohort.js @@ -55,8 +55,7 @@ export const changeStudentCohort = async (req, res) => { } try { - const changeResult = await Student.changeCohort(studentId, newCohortId) - console.log(changeResult) + await Student.changeCohort(studentId, newCohortId) return sendMessageResponse(res, 200, 'Student cohort changed successfully') } catch (e) { console.error('Error changing student cohort:', e) From 52f20bc93df6785e2b1e0560243a730b6a4940d7 Mon Sep 17 00:00:00 2001 From: LAVINIABENZAR Date: Tue, 20 Feb 2024 05:17:22 -0800 Subject: [PATCH 162/216] mistakes corrected --- docs/openapi.yml | 2 +- src/controllers/cohort.js | 2 +- src/domain/student.js | 2 +- src/routes/student.js | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/openapi.yml b/docs/openapi.yml index eaffc2ee..74073423 100644 --- a/docs/openapi.yml +++ b/docs/openapi.yml @@ -123,7 +123,7 @@ paths: schema: $ref: '#/components/schemas/Error' - /students: + /students: get: tags: - students diff --git a/src/controllers/cohort.js b/src/controllers/cohort.js index 44da7261..12c41b4b 100644 --- a/src/controllers/cohort.js +++ b/src/controllers/cohort.js @@ -1,5 +1,5 @@ import { createCohort, Cohort } from '../domain/cohort.js' -import { Student } from '../domain/student.js' +import Student from '../domain/student.js' import { sendDataResponse, sendMessageResponse } from '../utils/responses.js' export const create = async (req, res) => { diff --git a/src/domain/student.js b/src/domain/student.js index acf2cbbb..ece1b96d 100644 --- a/src/domain/student.js +++ b/src/domain/student.js @@ -1,6 +1,6 @@ import dbClient from '../utils/dbClient.js' -export class Student { +export default class Student { constructor( id = null, title = null, diff --git a/src/routes/student.js b/src/routes/student.js index cf61468c..02fa0b14 100644 --- a/src/routes/student.js +++ b/src/routes/student.js @@ -1,6 +1,6 @@ import { Router } from 'express' -import { getAllStudents } from '../controllers/student' -import { validateAuthentication } from '../middleware/auth' +import { getAllStudents } from '../controllers/student.js' +import { validateAuthentication } from '../middleware/auth.js' const router = Router() From 03e880f97513376a9da61cdf11ea38965cb54be8 Mon Sep 17 00:00:00 2001 From: Kye Date: Tue, 20 Feb 2024 14:14:17 +0000 Subject: [PATCH 163/216] add _findManyWhere to Teacher class --- src/domain/teachers.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/domain/teachers.js b/src/domain/teachers.js index 1be86129..c97953be 100644 --- a/src/domain/teachers.js +++ b/src/domain/teachers.js @@ -40,6 +40,22 @@ export default class Teacher { }) } + static async _findManyWhere(key, value) { + return dbClient.teacher.findMany({ + where: { + [key]: value + }, + include: { + user: { + include: { + profile: true + } + }, + department: true + } + }) + } + static async getTeacherBy(teacherId) { const teacher = await Teacher._findUnique(teacherId) console.log(teacher) From 18977c8728a0255db7f629629b3bb3d428a01054 Mon Sep 17 00:00:00 2001 From: Satoki Date: Tue, 20 Feb 2024 14:20:28 +0000 Subject: [PATCH 164/216] add PUT req route for user router --- src/routes/user.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/routes/user.js b/src/routes/user.js index cfef29f8..b9d5e542 100644 --- a/src/routes/user.js +++ b/src/routes/user.js @@ -18,5 +18,6 @@ router.get('/', validateAuthentication, getAll) router.get('/me', validateAuthentication, getSelf) router.get('/:id', validateAuthentication, getById) router.patch('/:id', validateAuthentication, validateTeacherRole, updateById) +router.put('/:id', validateAuthentication, updateById) export default router From 1fd976603975693a85aea1a260a33e4a7d2e99d4 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Tue, 20 Feb 2024 14:50:28 +0000 Subject: [PATCH 165/216] feat: route GET/teachers/me --- src/routes/teachers.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/routes/teachers.js b/src/routes/teachers.js index 72bf0b24..340ff389 100644 --- a/src/routes/teachers.js +++ b/src/routes/teachers.js @@ -1,10 +1,13 @@ import { Router } from 'express' -import { getAllTeachers, getTeacher } from '../controllers/teachers.js' -import { validateAuthentication } from '../middleware/auth.js' +import { getAllTeachers, getSelf, getTeacher } from '../controllers/teachers.js' +import { + validateAuthentication, + validateTeacherRole +} from '../middleware/auth.js' const router = Router() router.get('/', validateAuthentication, getAllTeachers) router.get('/:id', validateAuthentication, getTeacher) - +router.get('/me', validateAuthentication, validateTeacherRole, getSelf) export default router From 12509df80193fabea390ece20504f210cff9b0bd Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Tue, 20 Feb 2024 14:51:02 +0000 Subject: [PATCH 166/216] feat: getSelf() - teacher --- src/controllers/teachers.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/controllers/teachers.js b/src/controllers/teachers.js index b9a309d4..d91b66df 100644 --- a/src/controllers/teachers.js +++ b/src/controllers/teachers.js @@ -12,3 +12,5 @@ export const getTeacher = async (req, res) => { const teacher = await Teacher.getTeacherBy(id) return sendDataResponse(res, 200, { teacher }) } + +export const getSelf = async (req, res) => res.json({ data: req.teacher }) From 6ed2f28528f99fa75a3a36af5cbe20b0916db12e Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Tue, 20 Feb 2024 14:51:27 +0000 Subject: [PATCH 167/216] feat: findByUserId - teacher --- src/domain/teachers.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/domain/teachers.js b/src/domain/teachers.js index 1be86129..74c4901a 100644 --- a/src/domain/teachers.js +++ b/src/domain/teachers.js @@ -11,10 +11,10 @@ export default class Teacher { return new Teacher(teacher.id, teacher.user, teacher.departmentId) } - static async _findUnique(id) { + static async _findUnique(key, value) { return dbClient.teacher.findUnique({ where: { - id: id + [key]: value }, include: { department: true, @@ -41,11 +41,15 @@ export default class Teacher { } static async getTeacherBy(teacherId) { - const teacher = await Teacher._findUnique(teacherId) - console.log(teacher) + const teacher = await Teacher._findUnique('teacherId', teacherId) return teacher } + static async findByUserId(userId) { + const teacher = await Teacher._findUnique('userId', userId) + return Teacher.fromDb(teacher) + } + static async getAll() { const foundTeachers = await Teacher._findMany() const allTeachers = foundTeachers.map(Teacher.fromDb) From 7c3fd40d0e2e3cc834f9ecdca27fa7336784bf9d Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Tue, 20 Feb 2024 14:51:46 +0000 Subject: [PATCH 168/216] feat: route GET/students/me --- src/routes/student.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/routes/student.js b/src/routes/student.js index 02fa0b14..fa0da6ee 100644 --- a/src/routes/student.js +++ b/src/routes/student.js @@ -1,9 +1,13 @@ import { Router } from 'express' -import { getAllStudents } from '../controllers/student.js' -import { validateAuthentication } from '../middleware/auth.js' +import { getAllStudents, getSelf } from '../controllers/student.js' +import { + validateAuthentication, + validateStudentRole +} from '../middleware/auth.js' const router = Router() router.get('/', validateAuthentication, getAllStudents) +router.get('/me', validateAuthentication, validateStudentRole, getSelf) export default router From df806363752e9bd7ea351c428d9a2141c367c588 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Tue, 20 Feb 2024 14:52:45 +0000 Subject: [PATCH 169/216] feat: getSelf() - student --- src/controllers/student.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/controllers/student.js b/src/controllers/student.js index 6c8ad96f..bd93ebf7 100644 --- a/src/controllers/student.js +++ b/src/controllers/student.js @@ -6,3 +6,5 @@ export const getAllStudents = async (req, res) => { return sendDataResponse(res, 200, { students }) } + +export const getSelf = async (req, res) => res.json({ data: req.student }) From 9b9d7c20e08f96f683a46326fc7c7349ac92ca60 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Tue, 20 Feb 2024 14:53:00 +0000 Subject: [PATCH 170/216] feat: findByUserId - student --- src/domain/student.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/domain/student.js b/src/domain/student.js index ece1b96d..25290fad 100644 --- a/src/domain/student.js +++ b/src/domain/student.js @@ -51,6 +51,22 @@ export default class Student { }) } + static async _findUniqueWhere(key, value) { + return dbClient.student.findUnique({ + where: { + [key]: value + }, + include: { + user: { + include: { + profile: true + } + }, + cohort: true + } + }) + } + static async getAll() { const foundStudents = await Student._findMany() @@ -58,6 +74,12 @@ export default class Student { return allStudents } + static async findByUserId(userId) { + const foundStudent = await Student._findUniqueWhere('userId', userId) + const student = Student.fromDb(foundStudent) + return student + } + static async getAllStudentsByCohortId(id) { const foundStudents = await Student._findManyWhere('cohortId', id) From 0f9e9bfce45bc9df1331b558ff0d3e8c0ad57a44 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Tue, 20 Feb 2024 14:54:27 +0000 Subject: [PATCH 171/216] feat: set req.teacher or req.student+ add validateStudentRole --- src/middleware/auth.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/middleware/auth.js b/src/middleware/auth.js index baffff47..e1a30120 100644 --- a/src/middleware/auth.js +++ b/src/middleware/auth.js @@ -2,6 +2,8 @@ import { sendDataResponse, sendMessageResponse } from '../utils/responses.js' import { JWT_SECRET } from '../utils/config.js' import jwt from 'jsonwebtoken' import User from '../domain/user.js' +import Teacher from '../domain/teachers.js' +import Student from '../domain/student.js' export async function validateTeacherRole(req, res, next) { if (!req.user) { @@ -17,6 +19,20 @@ export async function validateTeacherRole(req, res, next) { next() } +export async function validateStudentRole(req, res, next) { + if (!req.user) { + return sendMessageResponse(res, 500, 'Unable to verify user') + } + + if (req.user.role !== 'STUDENT') { + return sendDataResponse(res, 403, { + authorization: 'You are not authorized to perform this action' + }) + } + + next() +} + export async function validateAuthentication(req, res, next) { const header = req.header('authorization') @@ -48,6 +64,15 @@ export async function validateAuthentication(req, res, next) { req.user = foundUser + if (decodedToken.userRole === 'TEACHER') { + const foundTeacher = await Teacher.findByUserId(decodedToken.userId) + req.teacher = foundTeacher + } + if (decodedToken.userRole === 'STUDENT') { + const foundStudent = await Student.findByUserId(decodedToken.userId) + req.student = foundStudent + } + next() } From ed9f8733b9ce19199916a53b93e1c991b1dcd5d2 Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Tue, 20 Feb 2024 15:25:38 +0000 Subject: [PATCH 172/216] Move errorCreator in own file in helpers --- src/helpers/errorCreator.js | 7 +++++++ src/middleware/commentErrors.js | 7 +------ 2 files changed, 8 insertions(+), 6 deletions(-) create mode 100644 src/helpers/errorCreator.js diff --git a/src/helpers/errorCreator.js b/src/helpers/errorCreator.js new file mode 100644 index 00000000..512ca5f8 --- /dev/null +++ b/src/helpers/errorCreator.js @@ -0,0 +1,7 @@ +const errorCreator = (message, status) => { + const error = new Error(message) + error.status = status + return error +} + +export default errorCreator diff --git a/src/middleware/commentErrors.js b/src/middleware/commentErrors.js index ca6ed72a..3a1c4c51 100644 --- a/src/middleware/commentErrors.js +++ b/src/middleware/commentErrors.js @@ -1,10 +1,5 @@ import Post from '../domain/post.js' - -const errorCreator = (message, status) => { - const error = new Error(message) - error.status = status - return error -} +import errorCreator from '../helpers/errorCreator.js' export const checkFields = (requiredFields) => { return (req, res, next) => { From ad67d0a293b1720098fc32bb6a7e8d65b7b81652 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Tue, 20 Feb 2024 15:50:20 +0000 Subject: [PATCH 173/216] docs: add GET/teachers/ma and GET/students/me --- docs/openapi.yml | 94 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 90 insertions(+), 4 deletions(-) diff --git a/docs/openapi.yml b/docs/openapi.yml index 74073423..8db6f935 100644 --- a/docs/openapi.yml +++ b/docs/openapi.yml @@ -65,6 +65,7 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' + /teachers: get: tags: @@ -123,10 +124,39 @@ paths: schema: $ref: '#/components/schemas/Error' + /teachers/me: + get: + tags: + - teacher + summary: Get a teacher by userId + description: '' + operationId: getTeacherByUserId + security: + - bearerAuth: [] + responses: + '200': + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/Teacher' + '400': + description: fail + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: fail + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /students: get: tags: - - students + - student summary: Get all students description: '' operationId: getAllStudents @@ -151,7 +181,36 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' - + + /students/me: + get: + tags: + - student + summary: Get a student by userId + description: '' + operationId: getStudentByUserId + security: + - bearerAuth: [] + responses: + '200': + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/Student' + '400': + description: fail + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: fail + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /login: post: tags: @@ -175,6 +234,7 @@ paths: '400': description: Invalid username/password supplied + /users/{id}: get: tags: @@ -250,6 +310,7 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' + /posts: post: tags: @@ -362,6 +423,7 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' + /logs: post: tags: @@ -402,6 +464,7 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' + /cohorts: post: tags: @@ -613,8 +676,6 @@ components: type: string updatedAt: type: string - user: - $ref: '#/components/schemas/User' department: type: object @@ -646,6 +707,29 @@ components: title: type: object + Student: + type: object + properties: + status: + type: string + data: + type: object + properties: + id: + type: integer + userId: + type: integer + cohortId: + type: integer + createdAt: + type: string + updatedAt: + type: string + user: + type: object + title: + type: object + User: type: object properties: @@ -856,6 +940,7 @@ components: type: string githubUrl: type: string + login: type: object properties: @@ -891,6 +976,7 @@ components: type: string githubUrl: type: string + Error: type: object properties: From 10030832afa83d25c4a3e3d03b65016371c636a7 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Tue, 20 Feb 2024 15:51:05 +0000 Subject: [PATCH 174/216] feat: GET/students/me includes cohort name and dep id --- src/domain/student.js | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/domain/student.js b/src/domain/student.js index 25290fad..ba019b3d 100644 --- a/src/domain/student.js +++ b/src/domain/student.js @@ -6,12 +6,14 @@ export default class Student { title = null, user = null, userId = null, + cohort = null, cohortId = null ) { this.id = id this.title = title this.user = user this.userId = userId + this.cohort = cohort this.cohortId = cohortId } @@ -21,6 +23,7 @@ export default class Student { student.title, student.user, student.userId, + student.cohort, student.cohortId ) } @@ -30,7 +33,9 @@ export default class Student { include: { cohort: true, user: { - profile: true + select: { + profile: true + } } } }) @@ -43,7 +48,7 @@ export default class Student { }, include: { user: { - include: { + select: { profile: true } } @@ -58,11 +63,16 @@ export default class Student { }, include: { user: { - include: { + select: { profile: true } }, - cohort: true + cohort: { + select: { + name: true, + departmentId: true + } + } } }) } @@ -114,7 +124,8 @@ export default class Student { firstName: this.user.profile.firstName, lastName: this.user.profile.lastName, userId: this.userId, - cohortId: this.cohortId + cohortId: this.cohortId, + cohort: this.cohort } } } From 4a8fae4e266c701d9a35e3c928c0ca28d94bd5ed Mon Sep 17 00:00:00 2001 From: Satoki Date: Tue, 20 Feb 2024 16:04:53 +0000 Subject: [PATCH 175/216] updated profile prisma schema model --- .../20240220160214_profile/migration.sql | 9 ++++++ prisma/schema.prisma | 28 ++++++++++++------- src/domain/user.js | 10 ++++++- src/routes/user.js | 5 ++-- 4 files changed, 39 insertions(+), 13 deletions(-) create mode 100644 prisma/migrations/20240220160214_profile/migration.sql diff --git a/prisma/migrations/20240220160214_profile/migration.sql b/prisma/migrations/20240220160214_profile/migration.sql new file mode 100644 index 00000000..f364982f --- /dev/null +++ b/prisma/migrations/20240220160214_profile/migration.sql @@ -0,0 +1,9 @@ +-- AlterTable +ALTER TABLE "Profile" ADD COLUMN "cohort" TEXT, +ADD COLUMN "email" TEXT, +ADD COLUMN "endDate" TEXT, +ADD COLUMN "mobile" TEXT, +ADD COLUMN "password" TEXT, +ADD COLUMN "role" TEXT, +ADD COLUMN "specialism" TEXT, +ADD COLUMN "startDate" TEXT; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 7ec4d7b2..88ed8820 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -44,16 +44,24 @@ model Student { } 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? - imageUrl 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? + imageUrl String? + role String? + specialism String? + cohort String? + startDate String? + endDate String? + email String? + mobile String? + password String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt } model Teacher { diff --git a/src/domain/user.js b/src/domain/user.js index ecbd9c23..7eb4fada 100644 --- a/src/domain/user.js +++ b/src/domain/user.js @@ -117,7 +117,15 @@ export default class User { lastName: this.lastName, bio: this.bio, githubUrl: this.githubUrl, - imageUrl: this.imageUrl + imageUrl: this.imageUrl, + role: this.role, + specialism: this.specialism, + cohort: this.cohort, + startDate: this.startDate, + endDate: this.endDate, + email: this.email, + mobile: this.mobile, + password: this.passwordHash } } diff --git a/src/routes/user.js b/src/routes/user.js index b9d5e542..46dfdbb4 100644 --- a/src/routes/user.js +++ b/src/routes/user.js @@ -4,7 +4,8 @@ import { getById, getSelf, getAll, - updateById + updateById, + createProfile } from '../controllers/user.js' import { validateAuthentication, @@ -18,6 +19,6 @@ router.get('/', validateAuthentication, getAll) router.get('/me', validateAuthentication, getSelf) router.get('/:id', validateAuthentication, getById) router.patch('/:id', validateAuthentication, validateTeacherRole, updateById) -router.put('/:id', validateAuthentication, updateById) +router.put('/:id', validateAuthentication, createProfile) export default router From a97dcae4f6f5a83c84920f19c4e9e35cffefd399 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Tue, 20 Feb 2024 16:22:55 +0000 Subject: [PATCH 176/216] feat: teacher route: include department and its name --- src/domain/teachers.js | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/domain/teachers.js b/src/domain/teachers.js index 74c4901a..ad0b39de 100644 --- a/src/domain/teachers.js +++ b/src/domain/teachers.js @@ -1,14 +1,20 @@ import dbClient from '../utils/dbClient.js' export default class Teacher { - constructor(id = null, user = null, departmentId = null) { + constructor(id = null, user = null, departmentId = null, department = null) { this.id = id this.user = user this.departmentId = departmentId + this.department = department } static fromDb(teacher) { - return new Teacher(teacher.id, teacher.user, teacher.departmentId) + return new Teacher( + teacher.id, + teacher.user, + teacher.departmentId, + teacher.department + ) } static async _findUnique(key, value) { @@ -17,9 +23,13 @@ export default class Teacher { [key]: value }, include: { - department: true, + department: { + select: { + name: true + } + }, user: { - include: { + select: { profile: true } } @@ -41,7 +51,7 @@ export default class Teacher { } static async getTeacherBy(teacherId) { - const teacher = await Teacher._findUnique('teacherId', teacherId) + const teacher = await Teacher._findUnique('id', teacherId) return teacher } From f6adff10976cf72aad8e701f62e129748170ad4e Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Tue, 20 Feb 2024 16:23:44 +0000 Subject: [PATCH 177/216] fix: re-order routes --- src/routes/teachers.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/routes/teachers.js b/src/routes/teachers.js index 340ff389..8be04421 100644 --- a/src/routes/teachers.js +++ b/src/routes/teachers.js @@ -8,6 +8,7 @@ import { const router = Router() router.get('/', validateAuthentication, getAllTeachers) -router.get('/:id', validateAuthentication, getTeacher) router.get('/me', validateAuthentication, validateTeacherRole, getSelf) +router.get('/:id', validateAuthentication, getTeacher) + export default router From 4074b1b151867710ea583ba6c92b2e31a235852f Mon Sep 17 00:00:00 2001 From: Eduard Bissell Date: Tue, 20 Feb 2024 16:36:02 +0000 Subject: [PATCH 178/216] feat: add note model --- .../20240220163315_add_note_model/migration.sql | 16 ++++++++++++++++ prisma/schema.prisma | 12 ++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 prisma/migrations/20240220163315_add_note_model/migration.sql diff --git a/prisma/migrations/20240220163315_add_note_model/migration.sql b/prisma/migrations/20240220163315_add_note_model/migration.sql new file mode 100644 index 00000000..99f59bbe --- /dev/null +++ b/prisma/migrations/20240220163315_add_note_model/migration.sql @@ -0,0 +1,16 @@ +-- CreateTable +CREATE TABLE "Note" ( + "id" SERIAL NOT NULL, + "title" TEXT NOT NULL, + "content" TEXT NOT NULL, + "studentId" INTEGER NOT NULL, + "teacherId" INTEGER NOT NULL, + + CONSTRAINT "Note_pkey" PRIMARY KEY ("id") +); + +-- AddForeignKey +ALTER TABLE "Note" ADD CONSTRAINT "Note_studentId_fkey" FOREIGN KEY ("studentId") REFERENCES "Student"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Note" ADD CONSTRAINT "Note_teacherId_fkey" FOREIGN KEY ("teacherId") REFERENCES "Teacher"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 7ec4d7b2..99e6f53c 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -39,6 +39,7 @@ model Student { user User @relation(fields: [userId], references: [id]) cohortId Int? cohort Cohort? @relation(fields: [cohortId], references: [id]) + notes Note[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } @@ -62,6 +63,7 @@ model Teacher { user User @relation(fields: [userId], references: [id]) departmentId Int department Department @relation(fields: [departmentId], references: [id]) + notes Note[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } @@ -138,3 +140,13 @@ model DeliveryLogLine { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } + +model Note { + id Int @id @default(autoincrement()) + title String + content String + studentId Int + student Student @relation(fields: [studentId], references: [id], onDelete: Cascade) + teacherId Int + teacher Teacher @relation(fields: [teacherId], references: [id]) +} From f999f707425bdbafd088cab5ec5d53101af9fa24 Mon Sep 17 00:00:00 2001 From: Eduard Bissell Date: Tue, 20 Feb 2024 16:36:24 +0000 Subject: [PATCH 179/216] chore: add note seed data --- prisma/seed.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/prisma/seed.js b/prisma/seed.js index 5cedf31a..2c6792ce 100644 --- a/prisma/seed.js +++ b/prisma/seed.js @@ -67,6 +67,13 @@ async function seed() { ) await createPost(teacher1.id, 'Hello, students', [], [{ userId: 1 }]) + await createNote( + student1.id, + teacher1.id, + 'note on student 1', + 'they be learnin' + ) + process.exit(0) } @@ -94,6 +101,17 @@ async function createPost(userId, content, comments, likes) { return post } +async function createNote(studentUserId, teacherUserId, title, content) { + return await prisma.note.create({ + data: { + title, + content, + student: { connect: { userId: studentUserId } }, + teacher: { connect: { userId: teacherUserId } } + } + }) +} + async function createDepartment(name) { const department = await prisma.department.create({ data: { From aa9187f1fd094cb712db5bc630d05b8c54b916d9 Mon Sep 17 00:00:00 2001 From: Satoki Date: Tue, 20 Feb 2024 16:54:27 +0000 Subject: [PATCH 180/216] updated user controller and domain to handle profile update req --- src/controllers/user.js | 9 +++++++++ src/domain/user.js | 23 +++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/src/controllers/user.js b/src/controllers/user.js index 66b52ea7..02fc00e5 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -72,3 +72,12 @@ export const updateById = async (req, res) => { return sendDataResponse(res, 201, { user: { cohort_id: cohortId } }) } + +export const createProfile = async (req, res) => { + const { email } = req.body + + const existingUser = await User.findByEmail(email) + const profile = await User.createProfileDb(existingUser.id, req.body) + + return sendDataResponse(res, 201, { profile }) +} diff --git a/src/domain/user.js b/src/domain/user.js index 7eb4fada..2fe42850 100644 --- a/src/domain/user.js +++ b/src/domain/user.js @@ -244,4 +244,27 @@ export default class User { return foundUsers.map((user) => User.fromDb(user)) } + + static async createProfileDb(id, user) { + const createdProfile = await dbClient.profile.update({ + where: { + id + }, + data: { + firstName: user.firstName, + lastName: user.lastName, + githubUrl: user.githubUrl, + bio: user.bio, + role: user.role, + specialism: user.specialism, + cohort: user.cohort, + startDate: user.startDate, + endDate: user.endDate, + email: user.email, + mobile: user.mobile, + password: user.password + } + }) + return createdProfile + } } From b5f6051895306fa5b533ff51bd2a3c5950351255 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Tue, 20 Feb 2024 17:59:52 +0000 Subject: [PATCH 181/216] fix: contructor: arguments passed in correct order --- src/domain/comment.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/domain/comment.js b/src/domain/comment.js index cfe48808..e5691a65 100644 --- a/src/domain/comment.js +++ b/src/domain/comment.js @@ -25,8 +25,8 @@ export default class Comment { comment.content, comment.post.id, comment.user.id, - comment.createdAt, author, + comment.createdAt, comment.updatedAt ) } From 643a63a5103e4b1cbcbb9ef6636628807b89f0a5 Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Wed, 21 Feb 2024 00:17:03 +0000 Subject: [PATCH 182/216] Add new middleware postError and new function post exist --- src/middleware/postError.js | 18 ++++++++++++++++++ src/routes/post.js | 3 ++- 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 src/middleware/postError.js diff --git a/src/middleware/postError.js b/src/middleware/postError.js new file mode 100644 index 00000000..38d23d24 --- /dev/null +++ b/src/middleware/postError.js @@ -0,0 +1,18 @@ +import Post from '../domain/post.js' +import errorCreator from '../helpers/errorCreator.js' + +export const postExist = async (req, res, next) => { + const { postId } = req.params + + try { + const post = await Post.getById(postId) + + if (!post) { + throw errorCreator('Post not found', 404) + } + + next() + } catch (err) { + next(err) + } +} diff --git a/src/routes/post.js b/src/routes/post.js index 0015dc40..dff72f43 100644 --- a/src/routes/post.js +++ b/src/routes/post.js @@ -7,6 +7,7 @@ import { likePost } from '../controllers/post.js' import { validateAuthentication } from '../middleware/auth.js' +import { postExist } from '../middleware/postError.js' const router = Router() @@ -14,6 +15,6 @@ router.post('/', validateAuthentication, create) router.post('/:postId/like', validateAuthentication, likePost) router.get('/', validateAuthentication, getAll) router.put('/:postId', validateAuthentication, editPost) -router.delete('/:postId', validateAuthentication, deletePost) +router.delete('/:postId', postExist, deletePost) export default router From d313c8b4a1476ed4c4666f072ebb039cdc22c55a Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Wed, 21 Feb 2024 00:34:31 +0000 Subject: [PATCH 183/216] Add new error handler check owner of the post --- src/middleware/postError.js | 29 +++++++++++++++++++++++++++++ src/routes/post.js | 10 ++++++++-- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/middleware/postError.js b/src/middleware/postError.js index 38d23d24..9ce83a69 100644 --- a/src/middleware/postError.js +++ b/src/middleware/postError.js @@ -11,6 +11,35 @@ export const postExist = async (req, res, next) => { throw errorCreator('Post not found', 404) } + const postData = { + id: post.id, + userId: post.userId + } + + req.post = postData + + next() + } catch (err) { + next(err) + } +} + +export const checkPostOwner = async (req, res, next) => { + const postUserId = req.post.userId + const userId = req.user.id + const userRole = req.user.role + + console.log(postUserId, userId, userRole) + + try { + if (userRole === 'TEACHER') { + return next() + } + + if (postUserId !== userId) { + throw errorCreator('You are not authorized to delete this post', 403) + } + next() } catch (err) { next(err) diff --git a/src/routes/post.js b/src/routes/post.js index dff72f43..f8ee9111 100644 --- a/src/routes/post.js +++ b/src/routes/post.js @@ -7,7 +7,7 @@ import { likePost } from '../controllers/post.js' import { validateAuthentication } from '../middleware/auth.js' -import { postExist } from '../middleware/postError.js' +import { checkPostOwner, postExist } from '../middleware/postError.js' const router = Router() @@ -15,6 +15,12 @@ router.post('/', validateAuthentication, create) router.post('/:postId/like', validateAuthentication, likePost) router.get('/', validateAuthentication, getAll) router.put('/:postId', validateAuthentication, editPost) -router.delete('/:postId', postExist, deletePost) +router.delete( + '/:postId', + validateAuthentication, + postExist, + checkPostOwner, + deletePost +) export default router From 8c603f252203dadd7edf9b18258e804ccbe74353 Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Wed, 21 Feb 2024 00:36:35 +0000 Subject: [PATCH 184/216] Refactor post domain and controller files --- src/controllers/post.js | 19 ++++--------------- src/domain/post.js | 18 +++--------------- 2 files changed, 7 insertions(+), 30 deletions(-) diff --git a/src/controllers/post.js b/src/controllers/post.js index 7d93c82e..4ede9dca 100644 --- a/src/controllers/post.js +++ b/src/controllers/post.js @@ -24,22 +24,11 @@ export const getAll = async (req, res) => { } export const deletePost = async (req, res) => { - const postId = Number(req.params.postId) - const userId = req.user.id + const postId = req.post.id - try { - const result = await Post.deleteByIdAndUserId(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) - return sendDataResponse(res, 500, { error: 'Something went wrong' }) - } + const deletedPost = await Post.deleteById(postId) + + return sendDataResponse(res, 200, deletedPost) } export const editPost = async (req, res) => { diff --git a/src/domain/post.js b/src/domain/post.js index 32db5a3d..0aed23b2 100644 --- a/src/domain/post.js +++ b/src/domain/post.js @@ -75,22 +75,10 @@ export default class Post { }) } - static async deleteByIdAndUserId(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 - } - } + static async deleteById(postId) { + const deletedPost = await dbClient.post.delete({ where: { id: postId } }) - await dbClient.post.delete({ where: { id: postId } }) - return { message: 'Post deleted successfully' } + return deletedPost } static async updateByIdAndUserId(postId, userId, content) { From 2e7af35f320184aa7bdb57e9b291495cbb0cd2d0 Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Wed, 21 Feb 2024 00:46:44 +0000 Subject: [PATCH 185/216] Update likes schema for cascade deleting --- prisma/schema.prisma | 60 ++++++++++++++++++++++---------------------- src/domain/post.js | 4 ++- 2 files changed, 33 insertions(+), 31 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 7ec4d7b2..76fa2f65 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -33,14 +33,14 @@ model User { } model Student { - id Int @id @default(autoincrement()) - title String - userId Int @unique - user User @relation(fields: [userId], references: [id]) - cohortId Int? - cohort Cohort? @relation(fields: [cohortId], references: [id]) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + id Int @id @default(autoincrement()) + title String + userId Int @unique + user User @relation(fields: [userId], references: [id]) + cohortId Int? + cohort Cohort? @relation(fields: [cohortId], references: [id]) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt } model Profile { @@ -57,35 +57,35 @@ model Profile { } model Teacher { - id Int @id @default(autoincrement()) - userId Int @unique - user User @relation(fields: [userId], references: [id]) - departmentId Int - department Department @relation(fields: [departmentId], references: [id]) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + id Int @id @default(autoincrement()) + userId Int @unique + user User @relation(fields: [userId], references: [id]) + departmentId Int + department Department @relation(fields: [departmentId], references: [id]) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt } model Cohort { - id Int @id @default(autoincrement()) - name String - students Student[] - departmentId Int - department Department @relation(fields: [departmentId], references: [id]) - deliveryLogs DeliveryLog[] - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt -} - -model Department { id Int @id @default(autoincrement()) - name String @unique - teachers Teacher[] - cohorts Cohort[] + name String + students Student[] + departmentId Int + department Department @relation(fields: [departmentId], references: [id]) + deliveryLogs DeliveryLog[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } +model Department { + id Int @id @default(autoincrement()) + name String @unique + teachers Teacher[] + cohorts Cohort[] + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + model Post { id Int @id @default(autoincrement()) content String @@ -100,7 +100,7 @@ model Post { model Like { id Int @id @default(autoincrement()) postId Int - post Post @relation(fields: [postId], references: [id]) + post Post @relation(fields: [postId], references: [id], onDelete: Cascade) userId Int user User @relation(fields: [userId], references: [id]) createdAt DateTime @default(now()) diff --git a/src/domain/post.js b/src/domain/post.js index 0aed23b2..ccfe23e3 100644 --- a/src/domain/post.js +++ b/src/domain/post.js @@ -76,7 +76,9 @@ export default class Post { } static async deleteById(postId) { - const deletedPost = await dbClient.post.delete({ where: { id: postId } }) + const deletedPost = await dbClient.post.delete({ + where: { id: Number(postId) } + }) return deletedPost } From cd7114b79d91d263f9e1cc3177c7229b0840db0e Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Wed, 21 Feb 2024 00:49:16 +0000 Subject: [PATCH 186/216] Add migration for last changes --- .../migration.sql | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 prisma/migrations/20240221004845_add_cascade_deleting_for_likes/migration.sql diff --git a/prisma/migrations/20240221004845_add_cascade_deleting_for_likes/migration.sql b/prisma/migrations/20240221004845_add_cascade_deleting_for_likes/migration.sql new file mode 100644 index 00000000..dfdb21c3 --- /dev/null +++ b/prisma/migrations/20240221004845_add_cascade_deleting_for_likes/migration.sql @@ -0,0 +1,5 @@ +-- DropForeignKey +ALTER TABLE "Like" DROP CONSTRAINT "Like_postId_fkey"; + +-- AddForeignKey +ALTER TABLE "Like" ADD CONSTRAINT "Like_postId_fkey" FOREIGN KEY ("postId") REFERENCES "Post"("id") ON DELETE CASCADE ON UPDATE CASCADE; From e50049b7220ceab2730bbaeebfb3591dcc2a7c88 Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Wed, 21 Feb 2024 00:53:12 +0000 Subject: [PATCH 187/216] Delete all console logs --- src/middleware/postError.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/middleware/postError.js b/src/middleware/postError.js index 9ce83a69..2fb34f0d 100644 --- a/src/middleware/postError.js +++ b/src/middleware/postError.js @@ -29,8 +29,6 @@ export const checkPostOwner = async (req, res, next) => { const userId = req.user.id const userRole = req.user.role - console.log(postUserId, userId, userRole) - try { if (userRole === 'TEACHER') { return next() From efb95d2eed4940e8e452380e0e931b6e29fbf68e Mon Sep 17 00:00:00 2001 From: Eduard Bissell Date: Wed, 21 Feb 2024 12:11:19 +0000 Subject: [PATCH 188/216] feat: add Note class --- src/domain/note.js | 79 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 src/domain/note.js diff --git a/src/domain/note.js b/src/domain/note.js new file mode 100644 index 00000000..5142ddf2 --- /dev/null +++ b/src/domain/note.js @@ -0,0 +1,79 @@ +import dbClient from '../utils/dbClient.js' + +export default class Note { + /** + * @param {number} id + * @param {string} title + * @param {string} content + * @param {{firstName: string, lastName: string, userId: number}} studentProfile + * @param {{firstName: string, lastName: string, userId: number}} teacherProfile + */ + constructor(id, title, content, studentProfile, teacherProfile) { + this.id = id + this.title = title + this.content = content + this.studentProfile = studentProfile + this.teacherProfile = teacherProfile + } + + static fromDb(note) { + return new Note( + note.id, + note.title, + note.content, + note.student.user.profile, + note.teacher.user.profile + ) + } + + /** + * @param {string} title + * @param {string} content + * @param {number} studentUserId + * @param {number} teacherUserId + * @returns {Promise} + */ + static async create(title, content, studentUserId, teacherUserId) { + const profileSelect = { + select: { + user: { + select: { + profile: { + select: { + userId: true, + firstName: true, + lastName: true + } + } + } + } + } + } + + const createdNote = await dbClient.note.create({ + data: { + title, + content, + student: { + connect: { + userId: studentUserId + } + }, + teacher: { + connect: { + userId: teacherUserId + } + } + }, + select: { + id: true, + title: true, + content: true, + student: profileSelect, + teacher: profileSelect + } + }) + + return Note.fromDb(createdNote) + } +} From 8614aa2336c4c15021a0da1c93a941d055f082ec Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Wed, 21 Feb 2024 15:14:54 +0000 Subject: [PATCH 189/216] Update docs file --- docs/openapi.yml | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/openapi.yml b/docs/openapi.yml index 8db6f935..eebab4e0 100644 --- a/docs/openapi.yml +++ b/docs/openapi.yml @@ -65,7 +65,7 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' - + /teachers: get: tags: @@ -209,7 +209,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Error' + $ref: '#/components/schemas/Error' /login: post: @@ -234,7 +234,7 @@ paths: '400': description: Invalid username/password supplied - + /users/{id}: get: tags: @@ -310,7 +310,7 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' - + /posts: post: tags: @@ -376,7 +376,7 @@ paths: tags: - post summary: Delete post - description: you can delete your own post + description: If you are user with role "STUDENT" you can delete only own post. If you are user with role "TEACHER" you will be able to delete any posts. operationId: deletePost security: - bearerAuth: [] @@ -423,7 +423,7 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' - + /logs: post: tags: @@ -464,7 +464,7 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' - + /cohorts: post: tags: @@ -656,9 +656,9 @@ components: user: type: object department: - type: object + type: object - Teacher: + Teacher: type: object properties: status: @@ -705,9 +705,9 @@ components: user: type: object title: - type: object - - Student: + type: object + + Student: type: object properties: status: @@ -728,7 +728,7 @@ components: user: type: object title: - type: object + type: object User: type: object @@ -940,7 +940,7 @@ components: type: string githubUrl: type: string - + login: type: object properties: @@ -976,7 +976,7 @@ components: type: string githubUrl: type: string - + Error: type: object properties: From 5250fbb724d17f129274504f34a49df5f64b3355 Mon Sep 17 00:00:00 2001 From: Eduard Bissell Date: Wed, 21 Feb 2024 16:00:28 +0000 Subject: [PATCH 190/216] feat: implement createNote controller --- src/controllers/note.js | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 src/controllers/note.js diff --git a/src/controllers/note.js b/src/controllers/note.js new file mode 100644 index 00000000..6f2807af --- /dev/null +++ b/src/controllers/note.js @@ -0,0 +1,41 @@ +import Note from '../domain/note.js' +import Student from '../domain/student.js' +import Teacher from '../domain/teachers.js' + +import { sendDataResponse } from '../utils/responses.js' + +export const createNote = async (req, res) => { + const { title, content, studentUserId, teacherUserId } = req.body + + try { + await Student.findByUserId(studentUserId) + } catch (error) { + return sendDataResponse(res, 404, { + error: 'Student not found', + status: 404 + }) + } + + try { + await Teacher.findByUserId(teacherUserId) + } catch (error) { + return sendDataResponse(res, 404, { + error: 'Teacher not found', + status: 404 + }) + } + + try { + const createdNote = await Note.create( + title, + content, + studentUserId, + teacherUserId + ) + + return sendDataResponse(res, 201, createdNote) + } catch (error) { + console.error('Error creating note:', error) + return sendDataResponse(res, 500, { error: 'Something went wrong' }) + } +} From 35ee7a1aba636979c9cb7287b5a4d1a29f946e27 Mon Sep 17 00:00:00 2001 From: Eduard Bissell Date: Wed, 21 Feb 2024 16:01:00 +0000 Subject: [PATCH 191/216] feat: implement post /notes route --- src/routes/note.js | 20 ++++++++++++++++++++ src/server.js | 2 ++ 2 files changed, 22 insertions(+) create mode 100644 src/routes/note.js diff --git a/src/routes/note.js b/src/routes/note.js new file mode 100644 index 00000000..0e50b05f --- /dev/null +++ b/src/routes/note.js @@ -0,0 +1,20 @@ +import { Router } from 'express' + +import { + validateAuthentication, + validateTeacherRole +} from '../middleware/auth.js' +import { createNote } from '../controllers/note.js' +import { checkFields } from '../middleware/commentErrors.js' + +const router = Router() + +router.post( + '/', + validateAuthentication, + validateTeacherRole, + checkFields(['title', 'content', 'studentUserId', 'teacherUserId']), + createNote +) + +export default router diff --git a/src/server.js b/src/server.js index 17d47cc5..8a62959b 100644 --- a/src/server.js +++ b/src/server.js @@ -13,6 +13,7 @@ import deliveryLogRouter from './routes/deliveryLog.js' import commentRouter from './routes/comment.js' import teachersRoute from './routes/teachers.js' import studentsRouter from './routes/student.js' +import notesRouter from './routes/note.js' const app = express() app.disable('x-powered-by') @@ -34,6 +35,7 @@ app.use('/', authRouter) app.use('/comments', commentRouter) app.use('/teachers', teachersRoute) app.use('/students', studentsRouter) +app.use('/notes', notesRouter) app.use((err, req, res, next) => { res.status(err.status ?? 500).json({ From 258daa8032f6702517058119db7093a1260769e5 Mon Sep 17 00:00:00 2001 From: Eduard Bissell Date: Wed, 21 Feb 2024 16:36:11 +0000 Subject: [PATCH 192/216] fix: correct http error code --- src/controllers/note.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/controllers/note.js b/src/controllers/note.js index 6f2807af..099d549f 100644 --- a/src/controllers/note.js +++ b/src/controllers/note.js @@ -10,18 +10,18 @@ export const createNote = async (req, res) => { try { await Student.findByUserId(studentUserId) } catch (error) { - return sendDataResponse(res, 404, { + return sendDataResponse(res, 400, { error: 'Student not found', - status: 404 + status: 400 }) } try { await Teacher.findByUserId(teacherUserId) } catch (error) { - return sendDataResponse(res, 404, { + return sendDataResponse(res, 400, { error: 'Teacher not found', - status: 404 + status: 400 }) } From 7bcd74c7e0877661ef06803143deda268e7f4801 Mon Sep 17 00:00:00 2001 From: Eduard Bissell Date: Wed, 21 Feb 2024 16:36:58 +0000 Subject: [PATCH 193/216] docs: add POST /notes --- docs/openapi.yml | 88 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/docs/openapi.yml b/docs/openapi.yml index 8db6f935..7bbb5d03 100644 --- a/docs/openapi.yml +++ b/docs/openapi.yml @@ -424,6 +424,48 @@ paths: schema: $ref: '#/components/schemas/Error' + /notes: + post: + tags: + - note + summary: Create note + description: Can only be done by users with the teacher role + operationId: postNote + security: + - bearerAuth: [] + requestBody: + description: Created note object + content: + application/json: + schema: + type: object + properties: + title: + type: string + example: Outstanding use of commas + content: + type: string + example: This student's mastery over using commas is unlike anything I've ever seen. To the extent I never want to use a comma again. + studentUserId: + type: integer + example: 1 + teacherUserId: + type: integer + example: 3 + responses: + '201': + description: success + content: + application/json: + schema: + $ref: '#/components/schemas/Note' + '400': + description: fail + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /logs: post: tags: @@ -914,6 +956,52 @@ components: type: string format: string + Note: + type: object + properties: + status: + type: string + example: success + data: + type: object + properties: + note: + type: object + properties: + id: + type: integer + example: 1 + title: + type: string + example: Outstanding use of commas + content: + type: string + example: This student's mastery over using commas is unlike anything I've ever seen. To the extent I never want to use a comma again. + student: + type: object + properties: + userId: + type: integer + example: 1 + firstName: + type: string + example: Joe + lastName: + type: string + example: Bloggs + teacher: + type: object + properties: + userId: + type: integer + example: 3 + firstName: + type: string + example: Rick + lastName: + type: string + example: Sanchez + CreatedUser: type: object properties: From a3f88ce5ebaef4e905812274908051ff0e21d208 Mon Sep 17 00:00:00 2001 From: Satoki Date: Wed, 21 Feb 2024 17:24:48 +0000 Subject: [PATCH 194/216] added error handling --- src/controllers/user.js | 14 +++++++++++--- src/domain/user.js | 25 +++++++++++++------------ 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/controllers/user.js b/src/controllers/user.js index 02fc00e5..905fb8ff 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -76,8 +76,16 @@ export const updateById = async (req, res) => { export const createProfile = async (req, res) => { const { email } = req.body - const existingUser = await User.findByEmail(email) - const profile = await User.createProfileDb(existingUser.id, req.body) + try { + const existingUser = await User.findByEmail(email) + + if (!existingUser) { + return sendDataResponse(res, 404, { error: 'User not found' }) + } - return sendDataResponse(res, 201, { profile }) + const profile = await User.createProfileDb(existingUser.id, req.body) + return sendDataResponse(res, 201, { profile }) + } catch (e) { + return sendMessageResponse(res, 500, 'Unable create user profile') + } } diff --git a/src/domain/user.js b/src/domain/user.js index 2fe42850..fc15813b 100644 --- a/src/domain/user.js +++ b/src/domain/user.js @@ -251,18 +251,19 @@ export default class User { id }, data: { - firstName: user.firstName, - lastName: user.lastName, - githubUrl: user.githubUrl, - bio: user.bio, - role: user.role, - specialism: user.specialism, - cohort: user.cohort, - startDate: user.startDate, - endDate: user.endDate, - email: user.email, - mobile: user.mobile, - password: user.password + firstName: user.firstName || 'unknown', + lastName: user.lastName || 'unknown', + githubUrl: user.githubUrl || 'unknown', + imageUrl: user.imageUrl || 'unknown', + bio: user.bio || 'unknown', + role: user.role || 'unknown', + specialism: user.specialism || 'unknown', + cohort: user.cohort || 'unknown', + startDate: user.startDate || 'unknown', + endDate: user.endDate || 'unknown', + email: user.email || 'unknown', + mobile: user.mobile || 'unknown', + password: user.password || 'unknown' } }) return createdProfile From 46c8e882d369e85b9f9bece012f1bfd93a8506ef Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Wed, 21 Feb 2024 17:45:46 +0000 Subject: [PATCH 195/216] fix: ensure postId is a number --- src/controllers/comment.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/comment.js b/src/controllers/comment.js index cb618e57..3561fa6e 100644 --- a/src/controllers/comment.js +++ b/src/controllers/comment.js @@ -17,7 +17,7 @@ export const createComment = async (req, res) => { } export const getCommentsByPost = async (req, res) => { - const { postId } = req.params + const postId = Number(req.params.postId) const comments = await Comment.getCommentsByPostId(postId) From 93978cb7e6bdb224ccd8ff69be9f6c66bdb46a00 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Wed, 21 Feb 2024 17:47:43 +0000 Subject: [PATCH 196/216] fix: comments include firstName and lastName of their author --- src/domain/comment.js | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/src/domain/comment.js b/src/domain/comment.js index e5691a65..ed8b8460 100644 --- a/src/domain/comment.js +++ b/src/domain/comment.js @@ -23,8 +23,8 @@ export default class Comment { return new Comment( comment.id, comment.content, - comment.post.id, - comment.user.id, + comment.postId, + comment.userId, author, comment.createdAt, comment.updatedAt @@ -51,6 +51,26 @@ export default class Comment { return comments } + static async _findManyWhere(key, value) { + return await dbClient.comment.findMany({ + where: { + [key]: value + }, + include: { + user: { + select: { + profile: { + select: { + firstName: true, + lastName: true + } + } + } + } + } + }) + } + async save() { const data = { content: this.content, @@ -92,12 +112,8 @@ export default class Comment { } static async getCommentsByPostId(postId) { - const foundComments = await dbClient.comment.findMany({ - where: { - postId: Number(postId) - } - }) - - return foundComments + const foundComments = await Comment._findManyWhere('postId', postId) + const commentList = foundComments.map(Comment.fromDb) + return commentList } } From ebacbef6598655990489b03910fbf80952933009 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Wed, 21 Feb 2024 17:51:58 +0000 Subject: [PATCH 197/216] docs: update comment schema --- docs/openapi.yml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/openapi.yml b/docs/openapi.yml index 8db6f935..e59911e8 100644 --- a/docs/openapi.yml +++ b/docs/openapi.yml @@ -867,13 +867,16 @@ components: type: integer userId: type: integer - author: + user: type: object properties: - firstName: - type: string - lastName: - type: string + profile: + type: object + properties: + firstName: + type: string + lastName: + type: string createdAt: type: string format: string From f76d828d120ef4ee2ef21c4aa601a1b950c26ee9 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Wed, 21 Feb 2024 17:58:09 +0000 Subject: [PATCH 198/216] docs: amend comment schema --- docs/openapi.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/docs/openapi.yml b/docs/openapi.yml index e59911e8..f411d6ba 100644 --- a/docs/openapi.yml +++ b/docs/openapi.yml @@ -867,12 +867,9 @@ components: type: integer userId: type: integer - user: + author: type: object properties: - profile: - type: object - properties: firstName: type: string lastName: From 7555b5d6c8d4710bbabae481830fd5b5cab6fc2a Mon Sep 17 00:00:00 2001 From: Nazar Tymiv Date: Wed, 21 Feb 2024 23:44:42 +0000 Subject: [PATCH 199/216] Refactor middleware of postErrors --- src/middleware/postError.js | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/src/middleware/postError.js b/src/middleware/postError.js index 2fb34f0d..c2f4aa2c 100644 --- a/src/middleware/postError.js +++ b/src/middleware/postError.js @@ -4,24 +4,24 @@ import errorCreator from '../helpers/errorCreator.js' export const postExist = async (req, res, next) => { const { postId } = req.params - try { - const post = await Post.getById(postId) + const post = await Post.getById(postId) + try { if (!post) { throw errorCreator('Post not found', 404) } - - const postData = { - id: post.id, - userId: post.userId - } - - req.post = postData - - next() } catch (err) { next(err) } + + const postData = { + id: post.id, + userId: post.userId + } + + req.post = postData + + next() } export const checkPostOwner = async (req, res, next) => { @@ -30,16 +30,12 @@ export const checkPostOwner = async (req, res, next) => { const userRole = req.user.role try { - if (userRole === 'TEACHER') { - return next() - } - - if (postUserId !== userId) { + if (postUserId !== userId && userRole !== 'TEACHER') { throw errorCreator('You are not authorized to delete this post', 403) } - - next() } catch (err) { next(err) } + + next() } From baaa44150c223b099a5469cdffede8a3ddc68018 Mon Sep 17 00:00:00 2001 From: Satoki Date: Thu, 22 Feb 2024 09:53:33 +0000 Subject: [PATCH 200/216] update fields from 'unknown' to null --- src/domain/user.js | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/domain/user.js b/src/domain/user.js index fc15813b..0bc9b146 100644 --- a/src/domain/user.js +++ b/src/domain/user.js @@ -251,19 +251,19 @@ export default class User { id }, data: { - firstName: user.firstName || 'unknown', - lastName: user.lastName || 'unknown', - githubUrl: user.githubUrl || 'unknown', - imageUrl: user.imageUrl || 'unknown', - bio: user.bio || 'unknown', - role: user.role || 'unknown', - specialism: user.specialism || 'unknown', - cohort: user.cohort || 'unknown', - startDate: user.startDate || 'unknown', - endDate: user.endDate || 'unknown', - email: user.email || 'unknown', - mobile: user.mobile || 'unknown', - password: user.password || 'unknown' + firstName: user.firstName || null, + lastName: user.lastName || null, + githubUrl: user.githubUrl || null, + imageUrl: user.imageUrl || null, + bio: user.bio || null, + role: user.role || null, + specialism: user.specialism || null, + cohort: user.cohort || null, + startDate: user.startDate || null, + endDate: user.endDate || null, + email: user.email || null, + mobile: user.mobile || null, + password: user.password || null } }) return createdProfile From 61006c2705710dc824d2f6a4a6543118f314559d Mon Sep 17 00:00:00 2001 From: callumhayden Date: Thu, 22 Feb 2024 10:07:01 +0000 Subject: [PATCH 201/216] updated the docs --- docs/openapi.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/docs/openapi.yml b/docs/openapi.yml index 70a864ef..d3a4dc90 100644 --- a/docs/openapi.yml +++ b/docs/openapi.yml @@ -344,6 +344,28 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' + put: + tags: + - cohort + summary: change a student's cohort + description: This can only be done by the logged in user with role TEACHER. + operationId: changeStudentCohort + security: + - bearerAuth: [] + responses: + 200: + description: success + content: + application/json: + schema: + type: object + properties: + status: + type: string + data: + properties: + cohort: + $ref: '#/components/schemas/Cohort' get: tags: - cohort From 2d50d28e9a97de0ef892157246a8d9e81c851135 Mon Sep 17 00:00:00 2001 From: Satoki Date: Thu, 22 Feb 2024 10:26:35 +0000 Subject: [PATCH 202/216] update api docs with new PUT request --- docs/openapi.yml | 106 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/docs/openapi.yml b/docs/openapi.yml index 7bbb5d03..deca37d8 100644 --- a/docs/openapi.yml +++ b/docs/openapi.yml @@ -311,6 +311,41 @@ paths: schema: $ref: '#/components/schemas/Error' + put: + tags: + - user + summary: Update a user profile + description: Allows a user to complete profile details after registering for the first time. + operationId: userUpdate + security: + - bearerAuth: [] + parameters: + - name: id + in: path + description: 'The user id of the profile' + required: true + schema: + type: string + requestBody: + description: The profile info + content: + application/json: + schema: + $ref: '#/components/schemas/UserProfile' + responses: + '201': + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/UpdatedProfile' + '404': + description: fail + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /posts: post: tags: @@ -833,6 +868,36 @@ components: type: string imageUrl: type: string + + UserProfile: + type: object + properties: + firstName: + type: string + lastName: + type: string + githubUrl: + type: string + imageUrl: + type: string + bio: + type: string + role: + type: string + specialism: + type: string + cohort: + type: string + startDate: + type: string + endDate: + type: string + email: + type: string + mobile: + type: string + password: + type: string Posts: type: object @@ -1108,3 +1173,44 @@ components: type: integer content: type: string + + UpdatedProfile: + type: object + properties: + status: + type: string + example: success + data: + properties: + profile: + properties: + id: + type: integer + userId: + type: integer + firstName: + type: string + lastName: + type: string + githubUrl: + type: string + imageUrl: + type: string + bio: + type: string + role: + type: string + specialism: + type: string + cohort: + type: string + startDate: + type: string + endDate: + type: string + email: + type: string + mobile: + type: string + password: + type: string From 284cc9c8bc849f269ae19aac01113d96671fb626 Mon Sep 17 00:00:00 2001 From: Satoki Date: Thu, 22 Feb 2024 12:25:35 +0000 Subject: [PATCH 203/216] remove null from firstName --- src/domain/user.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/domain/user.js b/src/domain/user.js index 0bc9b146..26833475 100644 --- a/src/domain/user.js +++ b/src/domain/user.js @@ -251,7 +251,7 @@ export default class User { id }, data: { - firstName: user.firstName || null, + firstName: user.firstName, lastName: user.lastName || null, githubUrl: user.githubUrl || null, imageUrl: user.imageUrl || null, From e4516431ce854e8f16fe65ac0dbb212acdd5ab02 Mon Sep 17 00:00:00 2001 From: Satoki Date: Thu, 22 Feb 2024 13:41:17 +0000 Subject: [PATCH 204/216] removed nullable fields --- src/domain/user.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/domain/user.js b/src/domain/user.js index 26833475..e58f8b73 100644 --- a/src/domain/user.js +++ b/src/domain/user.js @@ -252,18 +252,18 @@ export default class User { }, data: { firstName: user.firstName, - lastName: user.lastName || null, - githubUrl: user.githubUrl || null, - imageUrl: user.imageUrl || null, - bio: user.bio || null, - role: user.role || null, - specialism: user.specialism || null, - cohort: user.cohort || null, - startDate: user.startDate || null, - endDate: user.endDate || null, - email: user.email || null, - mobile: user.mobile || null, - password: user.password || null + lastName: user.lastName, + githubUrl: user.githubUrl, + imageUrl: user.imageUrl, + bio: user.bio, + role: user.role, + specialism: user.specialism, + cohort: user.cohort, + startDate: user.startDate, + endDate: user.endDate, + email: user.email, + mobile: user.mobile, + password: user.password } }) return createdProfile From 13eb6b87779089b6728a117fd91b4b88ebbf8354 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Thu, 22 Feb 2024 17:34:59 +0000 Subject: [PATCH 205/216] build: enum Role has a TBA value --- prisma/schema.prisma | 1 + 1 file changed, 1 insertion(+) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 798841be..3ac79b32 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -14,6 +14,7 @@ datasource db { enum Role { STUDENT TEACHER + TBA } model User { From 19fc1a9c36ba3d289567959e539712be4f8d2f20 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Thu, 22 Feb 2024 17:40:26 +0000 Subject: [PATCH 206/216] build: migration for adding 'tba' to enum Role --- .../20240222173653_add_tba_to_role_enum/migration.sql | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 prisma/migrations/20240222173653_add_tba_to_role_enum/migration.sql diff --git a/prisma/migrations/20240222173653_add_tba_to_role_enum/migration.sql b/prisma/migrations/20240222173653_add_tba_to_role_enum/migration.sql new file mode 100644 index 00000000..f757d97d --- /dev/null +++ b/prisma/migrations/20240222173653_add_tba_to_role_enum/migration.sql @@ -0,0 +1,2 @@ +-- AlterEnum +ALTER TYPE "Role" ADD VALUE 'TBA'; From 56884f793ab3d8462d72165aa5629ce50a3ec30e Mon Sep 17 00:00:00 2001 From: Satoki Date: Thu, 22 Feb 2024 17:41:40 +0000 Subject: [PATCH 207/216] add path to get user profile --- src/routes/user.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/routes/user.js b/src/routes/user.js index 46dfdbb4..d2707544 100644 --- a/src/routes/user.js +++ b/src/routes/user.js @@ -5,7 +5,8 @@ import { getSelf, getAll, updateById, - createProfile + createProfile, + getUserProfile } from '../controllers/user.js' import { validateAuthentication, @@ -20,5 +21,6 @@ router.get('/me', validateAuthentication, getSelf) router.get('/:id', validateAuthentication, getById) router.patch('/:id', validateAuthentication, validateTeacherRole, updateById) router.put('/:id', validateAuthentication, createProfile) +router.get('/profile/:id', validateAuthentication, getUserProfile) export default router From 758f12ce4a752d5991d7d5dd16300a67b61f79ee Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Thu, 22 Feb 2024 17:41:50 +0000 Subject: [PATCH 208/216] fix: change default value for this.role to 'TBA' --- src/domain/user.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/domain/user.js b/src/domain/user.js index e58f8b73..dedabbae 100644 --- a/src/domain/user.js +++ b/src/domain/user.js @@ -62,7 +62,7 @@ export default class User { githubUrl, imageUrl, passwordHash = null, - role = 'STUDENT' + role = 'TBA' ) { this.id = id this.cohortId = cohortId From affca5ab0ef4d189943f837a1b41a1d38d69f9aa Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Thu, 22 Feb 2024 17:46:48 +0000 Subject: [PATCH 209/216] feat: findByUserId() throws an error if no student/teacher found --- src/domain/student.js | 1 + src/domain/teachers.js | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/domain/student.js b/src/domain/student.js index ba019b3d..f05d8b74 100644 --- a/src/domain/student.js +++ b/src/domain/student.js @@ -86,6 +86,7 @@ export default class Student { static async findByUserId(userId) { const foundStudent = await Student._findUniqueWhere('userId', userId) + if (!foundStudent) throw new Error('No student connected to this user') const student = Student.fromDb(foundStudent) return student } diff --git a/src/domain/teachers.js b/src/domain/teachers.js index a7370dd5..382989ee 100644 --- a/src/domain/teachers.js +++ b/src/domain/teachers.js @@ -72,8 +72,9 @@ export default class Teacher { } static async findByUserId(userId) { - const teacher = await Teacher._findUnique('userId', userId) - return Teacher.fromDb(teacher) + const foundTeacher = await Teacher._findUnique('userId', userId) + if (!foundTeacher) throw new Error('No teacher connected to this user') + return Teacher.fromDb(foundTeacher) } static async getAll() { From e3887d3f3a90fa2f47f769ad259f72c0f1a68007 Mon Sep 17 00:00:00 2001 From: Satoki Date: Thu, 22 Feb 2024 17:58:59 +0000 Subject: [PATCH 210/216] add function getUserProfile in user controllers --- src/controllers/user.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/controllers/user.js b/src/controllers/user.js index 905fb8ff..94f28948 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -89,3 +89,19 @@ export const createProfile = async (req, res) => { return sendMessageResponse(res, 500, 'Unable create user profile') } } + +export const getUserProfile = async (req, res) => { + const id = parseInt(req.params.id) + + try { + const foundUserProfile = await User.findProfileById(id) + + if (!foundUserProfile) { + return sendDataResponse(res, 404, { error: 'User not found' }) + } + + return sendDataResponse(res, 200, foundUserProfile) + } catch (e) { + return sendMessageResponse(res, 500, 'Unable to get user') + } +} From 9ecda629305ecc6244a6007bd41629f89c676d61 Mon Sep 17 00:00:00 2001 From: Satoki Date: Thu, 22 Feb 2024 18:20:31 +0000 Subject: [PATCH 211/216] add findProfileById function in user domain --- src/controllers/user.js | 2 +- src/domain/user.js | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/controllers/user.js b/src/controllers/user.js index 94f28948..7fddac48 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -100,7 +100,7 @@ export const getUserProfile = async (req, res) => { return sendDataResponse(res, 404, { error: 'User not found' }) } - return sendDataResponse(res, 200, foundUserProfile) + return sendDataResponse(res, 200, { profile: foundUserProfile }) } catch (e) { return sendMessageResponse(res, 500, 'Unable to get user') } diff --git a/src/domain/user.js b/src/domain/user.js index e58f8b73..196a562a 100644 --- a/src/domain/user.js +++ b/src/domain/user.js @@ -268,4 +268,18 @@ export default class User { }) return createdProfile } + + static async findProfileById(id) { + const foundProfile = await dbClient.profile.findUnique({ + where: { + id + } + }) + + if (foundProfile) { + return foundProfile + } + + return null + } } From 1e9d9f32ce126c50d5f8e00ec7ccdacf35d425a2 Mon Sep 17 00:00:00 2001 From: Satoki Date: Fri, 23 Feb 2024 10:25:19 +0000 Subject: [PATCH 212/216] update api spec with user profile GET req --- docs/openapi.yml | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/docs/openapi.yml b/docs/openapi.yml index 46d68baa..82be0077 100644 --- a/docs/openapi.yml +++ b/docs/openapi.yml @@ -346,6 +346,47 @@ paths: schema: $ref: '#/components/schemas/Error' + /users/profile/{id}: + get: + tags: + - user + summary: Get user profile by user id + description: '' + operationId: getUserProfile + security: + - bearerAuth: [] + parameters: + - name: id + in: path + description: 'The id of the user's profile required.' + required: true + schema: + type: string + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: object + properties: + status: + type: string + data: + $ref: '#/components/schemas/User' + '404': + description: fail + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: fail + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + /posts: post: tags: From 57a7ff6b7f58b6645d824c6de381c2c894d9c6d0 Mon Sep 17 00:00:00 2001 From: Satoki Date: Fri, 23 Feb 2024 10:44:46 +0000 Subject: [PATCH 213/216] removed apostrophe causing error in api spec file --- docs/openapi.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/openapi.yml b/docs/openapi.yml index 82be0077..7f395971 100644 --- a/docs/openapi.yml +++ b/docs/openapi.yml @@ -358,7 +358,7 @@ paths: parameters: - name: id in: path - description: 'The id of the user's profile required.' + description: 'The id of the user profile required' required: true schema: type: string From 9300f5eb756fd0eda60d42d0b80c44705dbd2c13 Mon Sep 17 00:00:00 2001 From: Satoki Date: Fri, 23 Feb 2024 10:59:14 +0000 Subject: [PATCH 214/216] amend try/catch block in getUserProfile function --- src/controllers/user.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/controllers/user.js b/src/controllers/user.js index 7fddac48..1d7f4cd4 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -93,15 +93,17 @@ export const createProfile = async (req, res) => { export const getUserProfile = async (req, res) => { const id = parseInt(req.params.id) - try { - const foundUserProfile = await User.findProfileById(id) - - if (!foundUserProfile) { - return sendDataResponse(res, 404, { error: 'User not found' }) - } + let foundUserProfile - return sendDataResponse(res, 200, { profile: foundUserProfile }) + try { + foundUserProfile = await User.findProfileById(id) } catch (e) { return sendMessageResponse(res, 500, 'Unable to get user') } + + if (!foundUserProfile) { + return sendDataResponse(res, 404, { error: 'User not found' }) + } + + return sendDataResponse(res, 200, { profile: foundUserProfile }) } From f5e2f18d1195c05ce14661c53356c6149be5c028 Mon Sep 17 00:00:00 2001 From: LAVINIABENZAR Date: Fri, 23 Feb 2024 05:55:41 -0800 Subject: [PATCH 215/216] add department name --- src/domain/student.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/domain/student.js b/src/domain/student.js index f05d8b74..c45f0bc0 100644 --- a/src/domain/student.js +++ b/src/domain/student.js @@ -70,7 +70,12 @@ export default class Student { cohort: { select: { name: true, - departmentId: true + departmentId: true, + department: { + select: { + name: true + } + } } } } From 6d00ac886b719e29e389e3e8ad33cc0a880c1ed6 Mon Sep 17 00:00:00 2001 From: Chloe Zermatten Date: Fri, 23 Feb 2024 14:31:05 +0000 Subject: [PATCH 216/216] fix: include profile --- src/domain/post.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/domain/post.js b/src/domain/post.js index ccfe23e3..03564884 100644 --- a/src/domain/post.js +++ b/src/domain/post.js @@ -22,7 +22,9 @@ export default class Post { } }, include: { - user: true + user: { + select: { profile: true } + } } }) @@ -99,7 +101,14 @@ export default class Post { const updatedPost = await dbClient.post.update({ where: { id: postId }, - data: { content } + data: { content }, + include: { + user: { + select: { + profile: { select: { firstName: true, lastName: true } } + } + } + } }) return {