From a0e175a29f8163be37167cf6444b75c56f4c61be Mon Sep 17 00:00:00 2001 From: Huilensolis Date: Wed, 19 Jun 2024 17:00:52 -0300 Subject: [PATCH 01/12] fix ci --- .github/workflows/client-formatting-linting.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/client-formatting-linting.yml b/.github/workflows/client-formatting-linting.yml index d51fc34..c5dc991 100644 --- a/.github/workflows/client-formatting-linting.yml +++ b/.github/workflows/client-formatting-linting.yml @@ -25,11 +25,12 @@ jobs: - name: Format run: bun run format - name: Commit and push changes - uses: devops-infra/action-commit-push@master + - uses: EndBug/add-and-commit@v9 # You can change this to use a specific version. with: - github_token: ${{ secrets.GITHUB_TOKEN }} - commit_message: format code - target_branch: ${{ github.event.pull_request.head.ref }} + author_name: Github Action + author_email: mail@example.com + cwd: "./apps/client" + message: "format(client): formatt code" linting: needs: formatting runs-on: ubuntu-latest From 160d561564a1d90465845f34b2ad469c9e96924a Mon Sep 17 00:00:00 2001 From: Huilensolis Date: Wed, 19 Jun 2024 17:23:02 -0300 Subject: [PATCH 02/12] fix(cli): add permission to write --- .github/workflows/client-formatting-linting.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/client-formatting-linting.yml b/.github/workflows/client-formatting-linting.yml index c5dc991..c6ca6ab 100644 --- a/.github/workflows/client-formatting-linting.yml +++ b/.github/workflows/client-formatting-linting.yml @@ -7,6 +7,9 @@ on: - main workflow_dispatch: +permissions: + contents: read|write + jobs: formatting: runs-on: ubuntu-latest @@ -28,7 +31,6 @@ jobs: - uses: EndBug/add-and-commit@v9 # You can change this to use a specific version. with: author_name: Github Action - author_email: mail@example.com cwd: "./apps/client" message: "format(client): formatt code" linting: From a537cb5c7d9988ca770f282f9bd367834c51dd36 Mon Sep 17 00:00:00 2001 From: Huilensolis Date: Wed, 19 Jun 2024 17:26:21 -0300 Subject: [PATCH 03/12] fix(cli): client pipeline cwd --- .github/workflows/client-formatting-linting.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/client-formatting-linting.yml b/.github/workflows/client-formatting-linting.yml index c6ca6ab..0f9a53c 100644 --- a/.github/workflows/client-formatting-linting.yml +++ b/.github/workflows/client-formatting-linting.yml @@ -1,5 +1,5 @@ name: Client Formatting and Linting -run-name: Run Linter and Formatter on ${{ github.event.pull_request.title }} by ${{ github.event.sender.login }} +run-name: Run client Linter and Formatter on ${{ github.event.pull_request.title }} by ${{ github.event.sender.login }} on: pull_request: @@ -16,7 +16,7 @@ jobs: defaults: run: shell: bash - working-directory: apps/api + working-directory: apps/client environment: Preview steps: - uses: actions/checkout@v4 @@ -39,7 +39,7 @@ jobs: defaults: run: shell: bash - working-directory: apps/api + working-directory: apps/client environment: Preview steps: - uses: actions/checkout@v4 From af114551b464efb6c2948ea8254af017046a123b Mon Sep 17 00:00:00 2001 From: Huilensolis Date: Wed, 19 Jun 2024 17:28:42 -0300 Subject: [PATCH 04/12] fix(ci): commit cwd --- .github/workflows/client-formatting-linting.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/client-formatting-linting.yml b/.github/workflows/client-formatting-linting.yml index 0f9a53c..f4232dc 100644 --- a/.github/workflows/client-formatting-linting.yml +++ b/.github/workflows/client-formatting-linting.yml @@ -31,7 +31,6 @@ jobs: - uses: EndBug/add-and-commit@v9 # You can change this to use a specific version. with: author_name: Github Action - cwd: "./apps/client" message: "format(client): formatt code" linting: needs: formatting From 12caf283e4401f7a4e6aae613708e2d8fc83e5fc Mon Sep 17 00:00:00 2001 From: Huilensolis Date: Wed, 19 Jun 2024 17:39:48 -0300 Subject: [PATCH 05/12] fix(ci): syntax --- .github/workflows/client-formatting-linting.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/client-formatting-linting.yml b/.github/workflows/client-formatting-linting.yml index f4232dc..154e377 100644 --- a/.github/workflows/client-formatting-linting.yml +++ b/.github/workflows/client-formatting-linting.yml @@ -8,7 +8,7 @@ on: workflow_dispatch: permissions: - contents: read|write + contents: write jobs: formatting: From c0feb334014498447c479f9cbb82f943ad1cf7d2 Mon Sep 17 00:00:00 2001 From: Huilensolis Date: Wed, 19 Jun 2024 17:50:47 -0300 Subject: [PATCH 06/12] fix(ci): syntax --- .github/workflows/client-formatting-linting.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/client-formatting-linting.yml b/.github/workflows/client-formatting-linting.yml index 154e377..889e684 100644 --- a/.github/workflows/client-formatting-linting.yml +++ b/.github/workflows/client-formatting-linting.yml @@ -27,7 +27,6 @@ jobs: run: bun install - name: Format run: bun run format - - name: Commit and push changes - uses: EndBug/add-and-commit@v9 # You can change this to use a specific version. with: author_name: Github Action From f6d4bd7dbe1fd074a268498cba6c37629dcd08a1 Mon Sep 17 00:00:00 2001 From: Huilensolis Date: Wed, 19 Jun 2024 18:21:35 -0300 Subject: [PATCH 07/12] fix(ci): add push --- .github/workflows/client-formatting-linting.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/client-formatting-linting.yml b/.github/workflows/client-formatting-linting.yml index 889e684..b6eaccf 100644 --- a/.github/workflows/client-formatting-linting.yml +++ b/.github/workflows/client-formatting-linting.yml @@ -31,6 +31,7 @@ jobs: with: author_name: Github Action message: "format(client): formatt code" + push: true linting: needs: formatting runs-on: ubuntu-latest From cd22fd1852a2669c38f7815dae817f70a35b7b32 Mon Sep 17 00:00:00 2001 From: Huilensolis Date: Wed, 19 Jun 2024 18:34:42 -0300 Subject: [PATCH 08/12] fix(ci): add branch --- .github/workflows/client-formatting-linting.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/client-formatting-linting.yml b/.github/workflows/client-formatting-linting.yml index b6eaccf..6a43c82 100644 --- a/.github/workflows/client-formatting-linting.yml +++ b/.github/workflows/client-formatting-linting.yml @@ -20,6 +20,8 @@ jobs: environment: Preview steps: - uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.ref }} - uses: oven-sh/setup-bun@v1 with: bun-version: latest # or "latest", "canary", From 4cff78a2daa13c24620a47f7b58640922ef19a63 Mon Sep 17 00:00:00 2001 From: Github Action Date: Wed, 19 Jun 2024 21:35:17 +0000 Subject: [PATCH 09/12] format(client): formatt code --- bun.lockb | Bin 328952 -> 328944 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/bun.lockb b/bun.lockb index 3e41c9aa73d37b77c78c488ff7ef131d9f0dbfec..d1a24c65a4b8d1332da4bd25e91da83b89178c34 100755 GIT binary patch delta 1416 zcmXYvOH5Ni6o#jrUZEn9N(Ffew6ruTLI5v{XxxZ^Q0fCw5(BOjS0panDGO9wX-u4j z3&P5ki5f|D;R1Ex1_56Q8Uz#!Dk>Ns7^wIk`k&0XbLRWcoO#`a9`9U__e!Iqtt;Fe zc65iG{bB9!c?O+IIh0agEPP;trroDgtDg~TAkT`R)DW2qV(&{Q(~nw$nUW+GDJ z9n;qW-IZFfak3P|_sAy5nmvUm@s;UWK*KA7Qs2n3Frh@RZlwwRT0_)(W|B;2jPJ_vVFPknYk%qI%zBa)VcM2E^rHd(k&*We$e4 zr}_xRFkk?pEYL#Mgt!-lWr3q)5yX9D$4p;Hr><+IG!sGqYq3p-tpm~7ke+oX=b>l; zTu$PTj+W_Vrz77?Z_`!AZ|f9OY3m~QB|AR#cJ!33!wl7ORR7N+%NkBlP-i^q^;wG_l|Zg7m{%fQ;no`9jNRK$5X4capk6^rLA zUk-FtywF~dr6IpYHclpo9UyyY`o&^`k{42p(kn1@KJm6~W>Y%X5H676m*SSuW;rc`CGNvOJk^sTp%lEWS|6 zhtkDsngv5w1&Cv0-&p4dYash>UW>&KN&!ese5{{jL9i7lmxugCR)qXIU+K50D2Bxx zBy<%*9H;b$7r80-j`Jovon77H)VD*Gx$B=OunjFQSmsfSh#B;xy;j$j-S{7+JvbXZA19+ zwJ@7s*CP+mv6*!ZU_sXHB$G{Ez;)GNYym@8+Y#fatHrnnKx~>2RY Date: Wed, 19 Jun 2024 18:39:23 -0300 Subject: [PATCH 10/12] fix(ci): api linting & formatting pipeline --- .github/workflows/api-formatting-linting.yml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/api-formatting-linting.yml b/.github/workflows/api-formatting-linting.yml index 421011c..0bad534 100644 --- a/.github/workflows/api-formatting-linting.yml +++ b/.github/workflows/api-formatting-linting.yml @@ -18,7 +18,7 @@ jobs: steps: - uses: actions/checkout@v4 with: - github_token: ${{ secrets.GITHUB_TOKEN }} + ref: ${{ github.event.pull_request.head.ref }} - uses: oven-sh/setup-bun@v1 with: bun-version: latest # or "latest", "canary", @@ -26,12 +26,11 @@ jobs: run: bun install - name: Format run: bun run format - - name: Commit and push changes - uses: devops-infra/action-commit-push@master + - uses: EndBug/add-and-commit@v9 # You can change this to use a specific version. with: - github_token: ${{ secrets.GITHUB_TOKEN }} - commit_message: format code - target_branch: ${{ github.event.pull_request.head.ref }} + author_name: Github Action + message: "format(api): formatt code" + push: true linting: needs: formatting runs-on: ubuntu-latest From 01cd10713df770ef814cfc94c69c8be1c1a82f46 Mon Sep 17 00:00:00 2001 From: Huilensolis Date: Wed, 19 Jun 2024 18:42:03 -0300 Subject: [PATCH 11/12] fix(ci): pipeline permissions --- .github/workflows/api-formatting-linting.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/api-formatting-linting.yml b/.github/workflows/api-formatting-linting.yml index 0bad534..92dec6e 100644 --- a/.github/workflows/api-formatting-linting.yml +++ b/.github/workflows/api-formatting-linting.yml @@ -7,6 +7,9 @@ on: - main workflow_dispatch: +permissions: + contents: write + jobs: formatting: runs-on: ubuntu-latest From a48102a5598b0685b7c508e924bd85870d91d14a Mon Sep 17 00:00:00 2001 From: Github Action Date: Wed, 19 Jun 2024 21:42:27 +0000 Subject: [PATCH 12/12] format(api): formatt code --- .../migrations/meta/0002_snapshot.json | 290 +++++++++--------- .../database/migrations/meta/_journal.json | 52 ++-- .../api/src/features/entry/providers/index.ts | 216 ++++++------- apps/api/src/features/entry/router/index.ts | 270 ++++++++-------- .../src/features/entry/schema/entry.schema.ts | 58 ++-- .../plugins/cron/remove-entries/index.ts | 52 ++-- .../shared/plugins/cron/remove-users/index.ts | 50 +-- .../tests/integration/entry/delete.test.ts | 154 +++++----- apps/api/tests/integration/entry/get.test.ts | 214 ++++++------- .../api/tests/integration/entry/guard.test.ts | 240 +++++++-------- .../api/tests/integration/entry/patch.test.ts | 56 ++-- apps/api/tests/integration/entry/post.test.ts | 108 +++---- apps/api/tests/lib/constants/index.ts | 16 +- apps/api/tests/lib/entry/index.ts | 36 +-- 14 files changed, 903 insertions(+), 909 deletions(-) diff --git a/apps/api/src/config/database/migrations/meta/0002_snapshot.json b/apps/api/src/config/database/migrations/meta/0002_snapshot.json index 4830f57..3498dbe 100644 --- a/apps/api/src/config/database/migrations/meta/0002_snapshot.json +++ b/apps/api/src/config/database/migrations/meta/0002_snapshot.json @@ -1,149 +1,143 @@ { - "id": "71781081-042f-4718-bd3e-86fc59bd6f04", - "prevId": "310079ed-8707-4397-9545-ca476f27af94", - "version": "7", - "dialect": "postgresql", - "tables": { - "public.entry": { - "name": "entry", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "uuid", - "primaryKey": true, - "notNull": true, - "default": "gen_random_uuid()" - }, - "user_id": { - "name": "user_id", - "type": "uuid", - "primaryKey": false, - "notNull": true - }, - "title": { - "name": "title", - "type": "varchar(80)", - "primaryKey": false, - "notNull": true, - "default": "'Untintled'" - }, - "created_at": { - "name": "created_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true, - "default": "now()" - }, - "word_count": { - "name": "word_count", - "type": "integer", - "primaryKey": false, - "notNull": true, - "default": 0 - }, - "content": { - "name": "content", - "type": "json", - "primaryKey": false, - "notNull": false, - "default": "'{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\"}]}'::json" - }, - "is_private": { - "name": "is_private", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "end_date": { - "name": "end_date", - "type": "timestamp", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": { - "entry_user_id_users_id_fk": { - "name": "entry_user_id_users_id_fk", - "tableFrom": "entry", - "tableTo": "users", - "columnsFrom": [ - "user_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "cascade" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {} - }, - "public.users": { - "name": "users", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "uuid", - "primaryKey": true, - "notNull": true, - "default": "gen_random_uuid()" - }, - "name": { - "name": "name", - "type": "varchar(50)", - "primaryKey": false, - "notNull": true - }, - "email": { - "name": "email", - "type": "varchar(100)", - "primaryKey": false, - "notNull": true - }, - "password": { - "name": "password", - "type": "varchar(255)", - "primaryKey": false, - "notNull": true - }, - "end_date": { - "name": "end_date", - "type": "date", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": { - "users_email_unique": { - "name": "users_email_unique", - "nullsNotDistinct": false, - "columns": [ - "email" - ] - } - } - } - }, - "enums": {}, - "schemas": {}, - "_meta": { - "columns": {}, - "schemas": {}, - "tables": {} - } -} \ No newline at end of file + "id": "71781081-042f-4718-bd3e-86fc59bd6f04", + "prevId": "310079ed-8707-4397-9545-ca476f27af94", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.entry": { + "name": "entry", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "varchar(80)", + "primaryKey": false, + "notNull": true, + "default": "'Untintled'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "word_count": { + "name": "word_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "content": { + "name": "content", + "type": "json", + "primaryKey": false, + "notNull": false, + "default": "'{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\"}]}'::json" + }, + "is_private": { + "name": "is_private", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "end_date": { + "name": "end_date", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "entry_user_id_users_id_fk": { + "name": "entry_user_id_users_id_fk", + "tableFrom": "entry", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "end_date": { + "name": "end_date", + "type": "date", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "users_email_unique": { + "name": "users_email_unique", + "nullsNotDistinct": false, + "columns": ["email"] + } + } + } + }, + "enums": {}, + "schemas": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} diff --git a/apps/api/src/config/database/migrations/meta/_journal.json b/apps/api/src/config/database/migrations/meta/_journal.json index b651cc6..07adaf2 100644 --- a/apps/api/src/config/database/migrations/meta/_journal.json +++ b/apps/api/src/config/database/migrations/meta/_journal.json @@ -1,27 +1,27 @@ { - "version": "7", - "dialect": "postgresql", - "entries": [ - { - "idx": 0, - "version": "7", - "when": 1717206187404, - "tag": "0000_odd_betty_brant", - "breakpoints": true - }, - { - "idx": 1, - "version": "7", - "when": 1717982167466, - "tag": "0001_dear_the_initiative", - "breakpoints": true - }, - { - "idx": 2, - "version": "7", - "when": 1718756836142, - "tag": "0002_solid_the_anarchist", - "breakpoints": true - } - ] -} \ No newline at end of file + "version": "7", + "dialect": "postgresql", + "entries": [ + { + "idx": 0, + "version": "7", + "when": 1717206187404, + "tag": "0000_odd_betty_brant", + "breakpoints": true + }, + { + "idx": 1, + "version": "7", + "when": 1717982167466, + "tag": "0001_dear_the_initiative", + "breakpoints": true + }, + { + "idx": 2, + "version": "7", + "when": 1718756836142, + "tag": "0002_solid_the_anarchist", + "breakpoints": true + } + ] +} diff --git a/apps/api/src/features/entry/providers/index.ts b/apps/api/src/features/entry/providers/index.ts index c5644a3..b39f8d2 100644 --- a/apps/api/src/features/entry/providers/index.ts +++ b/apps/api/src/features/entry/providers/index.ts @@ -2,130 +2,130 @@ import { db } from "@/config/database"; import type { TReturnHanler } from "@/shared/models/promises"; import { and, eq, ilike, isNull } from "drizzle-orm"; import type { - TInsertEntry, - TNewEntry, - TReadEntry, + TInsertEntry, + TNewEntry, + TReadEntry, } from "../models/entry.models"; import { Entry } from "../schema"; export class EntryProvider { - static async getPrivateEntryById({ - entryId, - userId, - }: { - entryId: string; - userId: string; - }): Promise { - const [entry] = await db - .select() - .from(Entry) - .where(and(eq(Entry.id, entryId), eq(Entry.user_id, userId))); + static async getPrivateEntryById({ + entryId, + userId, + }: { + entryId: string; + userId: string; + }): Promise { + const [entry] = await db + .select() + .from(Entry) + .where(and(eq(Entry.id, entryId), eq(Entry.user_id, userId))); - return entry; - } + return entry; + } - static async getPublicEntryById({ - entryId, - }: { - entryId: string; - }): Promise { - const [entry] = await db.select().from(Entry).where(eq(Entry.id, entryId)); + static async getPublicEntryById({ + entryId, + }: { + entryId: string; + }): Promise { + const [entry] = await db.select().from(Entry).where(eq(Entry.id, entryId)); - return entry; - } + return entry; + } - static async getEntriesListByUserId(userId: string): Promise { - const entries = await db - .select() - .from(Entry) - .where(eq(Entry.user_id, userId)); + static async getEntriesListByUserId(userId: string): Promise { + const entries = await db + .select() + .from(Entry) + .where(eq(Entry.user_id, userId)); - return entries; - } + return entries; + } - static async getPrivateEntryListByTitle({ - title, - userId, - }: { - title: string; - userId: string; - }): Promise { - const entries = await db - .select() - .from(Entry) - .where(and(ilike(Entry.title, title), eq(Entry.user_id, userId))); + static async getPrivateEntryListByTitle({ + title, + userId, + }: { + title: string; + userId: string; + }): Promise { + const entries = await db + .select() + .from(Entry) + .where(and(ilike(Entry.title, title), eq(Entry.user_id, userId))); - return entries; - } + return entries; + } - static async createEntry({ - entry, - userId, - }: { - entry: TInsertEntry; - userId: TReadEntry["user_id"]; - }): Promise> { - const newEntryValues: TNewEntry = { - content: { - type: "doc", - content: [ - { - type: "paragraph", - }, - ], - }, - ...entry, - user_id: userId, - }; + static async createEntry({ + entry, + userId, + }: { + entry: TInsertEntry; + userId: TReadEntry["user_id"]; + }): Promise> { + const newEntryValues: TNewEntry = { + content: { + type: "doc", + content: [ + { + type: "paragraph", + }, + ], + }, + ...entry, + user_id: userId, + }; - try { - const [newEntry] = await db - .insert(Entry) - .values(newEntryValues) - .returning(); + try { + const [newEntry] = await db + .insert(Entry) + .values(newEntryValues) + .returning(); - if (!newEntry) throw new Error("Failed to create entry"); + if (!newEntry) throw new Error("Failed to create entry"); - return { error: null, data: newEntry }; - } catch (error) { - return { error: "error creating new entry", data: null }; - } - } + return { error: null, data: newEntry }; + } catch (error) { + return { error: "error creating new entry", data: null }; + } + } - static async deleteEntry({ - entryId, - userId, - }: { - entryId: string; - userId: string; - }): Promise<{ error: string | null }> { - try { - await db - .update(Entry) - .set({ end_date: new Date() }) - .where(and(eq(Entry.id, entryId), eq(Entry.user_id, userId))); - return { error: null }; - } catch (error) { - return { error: "unknown" }; - } - } + static async deleteEntry({ + entryId, + userId, + }: { + entryId: string; + userId: string; + }): Promise<{ error: string | null }> { + try { + await db + .update(Entry) + .set({ end_date: new Date() }) + .where(and(eq(Entry.id, entryId), eq(Entry.user_id, userId))); + return { error: null }; + } catch (error) { + return { error: "unknown" }; + } + } - static async updateEntry({ - values, - entryId, - }: { - entryId: TReadEntry["id"]; - values: TInsertEntry; - }): Promise<{ error: string | null }> { - try { - await db - .update(Entry) - .set(values) - .where(and(eq(Entry.id, entryId), isNull(Entry.end_date))); + static async updateEntry({ + values, + entryId, + }: { + entryId: TReadEntry["id"]; + values: TInsertEntry; + }): Promise<{ error: string | null }> { + try { + await db + .update(Entry) + .set(values) + .where(and(eq(Entry.id, entryId), isNull(Entry.end_date))); - return { error: null }; - } catch (error) { - return { error: "error updating entry" }; - } - } + return { error: null }; + } catch (error) { + return { error: "error updating entry" }; + } + } } diff --git a/apps/api/src/features/entry/router/index.ts b/apps/api/src/features/entry/router/index.ts index 8752980..6647891 100644 --- a/apps/api/src/features/entry/router/index.ts +++ b/apps/api/src/features/entry/router/index.ts @@ -5,139 +5,139 @@ import { EntryInsertSchema, EntrySafeSchema } from "../models/entry.models"; import { EntryProvider } from "../providers"; export const EntryRoutes = new Elysia().group("/entry", (app) => - app - .use(isAuthenticated) - .post( - "/", - async ({ user, body, set }) => { - try { - const { data: newEntry, error } = await EntryProvider.createEntry({ - entry: body, - userId: user.id, - }); - - if (error || !newEntry) - throw new Error(error ?? "error creating entry"); - - set.status = "Created"; - return { id: newEntry.id }; - } catch (e) { - return error("Internal Server Error", {}); - } - }, - { - body: EntryInsertSchema, - response: { 201: t.Object({ id: t.String() }), 500: t.Object({}) }, - }, - ) - .get( - "/", - async ({ user, set }) => { - try { - const unsafeUserEntries = await EntryProvider.getEntriesListByUserId( - user.id, - ); - - const safeEntries = unsafeUserEntries.map((entry) => { - const { entry: notDeletedEntry } = EntryAdapter.toNotDeleted(entry); - - if (!notDeletedEntry) { - throw new Error("entry is deleted"); - } - - const { safeEntry } = EntryAdapter.toSafeEntry(notDeletedEntry); - - return safeEntry; - }); - - set.status = "OK"; - return safeEntries; - } catch (e) { - return error("Internal Server Error", {}); - } - }, - { - response: { - 200: t.Array(EntrySafeSchema), - 500: t.Object({}), - }, - }, - ) - .get( - "/:id", - async ({ user, error, set, params }) => { - try { - const entry = await EntryProvider.getPrivateEntryById({ - entryId: params.id, - userId: user.id, - }); - - if (!entry) { - return error("Not Found", {}); - } - - const { entry: notDeletedEntry } = EntryAdapter.toNotDeleted(entry); - - if (!notDeletedEntry) { - throw new Error("Entry Deleted"); - } - - const { safeEntry } = EntryAdapter.toSafeEntry(notDeletedEntry); - - set.status = "OK"; - return safeEntry; - } catch (e) { - return error("Not Found", {}); - } - }, - { - params: t.Object({ id: t.String() }), - response: { 200: EntrySafeSchema, 404: t.Object({}) }, - }, - ) - .delete( - "/:id", - async ({ params: { id: entryId }, set, user }) => { - try { - const { error } = await EntryProvider.deleteEntry({ - entryId, - userId: user.id, - }); - - if (error) throw new Error(error); - - set.status = "Accepted"; - return {}; - } catch (e) { - return error("Internal Server Error", {}); - } - }, - { params: t.Object({ id: t.String() }) }, - ) - .patch( - "/:id", - async ({ params: { id }, set, body }) => { - try { - const { error } = await EntryProvider.updateEntry({ - entryId: id, - values: body, - }); - - if (error) throw new Error(error); - - set.status = "No Content"; - return {}; - } catch (e) { - return error("Internal Server Error", {}); - } - }, - { - params: t.Object({ id: t.String() }), - body: EntryInsertSchema, - response: { - 201: t.Object({}), - 500: t.Object({}), - }, - }, - ), + app + .use(isAuthenticated) + .post( + "/", + async ({ user, body, set }) => { + try { + const { data: newEntry, error } = await EntryProvider.createEntry({ + entry: body, + userId: user.id, + }); + + if (error || !newEntry) + throw new Error(error ?? "error creating entry"); + + set.status = "Created"; + return { id: newEntry.id }; + } catch (e) { + return error("Internal Server Error", {}); + } + }, + { + body: EntryInsertSchema, + response: { 201: t.Object({ id: t.String() }), 500: t.Object({}) }, + }, + ) + .get( + "/", + async ({ user, set }) => { + try { + const unsafeUserEntries = await EntryProvider.getEntriesListByUserId( + user.id, + ); + + const safeEntries = unsafeUserEntries.map((entry) => { + const { entry: notDeletedEntry } = EntryAdapter.toNotDeleted(entry); + + if (!notDeletedEntry) { + throw new Error("entry is deleted"); + } + + const { safeEntry } = EntryAdapter.toSafeEntry(notDeletedEntry); + + return safeEntry; + }); + + set.status = "OK"; + return safeEntries; + } catch (e) { + return error("Internal Server Error", {}); + } + }, + { + response: { + 200: t.Array(EntrySafeSchema), + 500: t.Object({}), + }, + }, + ) + .get( + "/:id", + async ({ user, error, set, params }) => { + try { + const entry = await EntryProvider.getPrivateEntryById({ + entryId: params.id, + userId: user.id, + }); + + if (!entry) { + return error("Not Found", {}); + } + + const { entry: notDeletedEntry } = EntryAdapter.toNotDeleted(entry); + + if (!notDeletedEntry) { + throw new Error("Entry Deleted"); + } + + const { safeEntry } = EntryAdapter.toSafeEntry(notDeletedEntry); + + set.status = "OK"; + return safeEntry; + } catch (e) { + return error("Not Found", {}); + } + }, + { + params: t.Object({ id: t.String() }), + response: { 200: EntrySafeSchema, 404: t.Object({}) }, + }, + ) + .delete( + "/:id", + async ({ params: { id: entryId }, set, user }) => { + try { + const { error } = await EntryProvider.deleteEntry({ + entryId, + userId: user.id, + }); + + if (error) throw new Error(error); + + set.status = "Accepted"; + return {}; + } catch (e) { + return error("Internal Server Error", {}); + } + }, + { params: t.Object({ id: t.String() }) }, + ) + .patch( + "/:id", + async ({ params: { id }, set, body }) => { + try { + const { error } = await EntryProvider.updateEntry({ + entryId: id, + values: body, + }); + + if (error) throw new Error(error); + + set.status = "No Content"; + return {}; + } catch (e) { + return error("Internal Server Error", {}); + } + }, + { + params: t.Object({ id: t.String() }), + body: EntryInsertSchema, + response: { + 201: t.Object({}), + 500: t.Object({}), + }, + }, + ), ); diff --git a/apps/api/src/features/entry/schema/entry.schema.ts b/apps/api/src/features/entry/schema/entry.schema.ts index f11ecb9..1730256 100644 --- a/apps/api/src/features/entry/schema/entry.schema.ts +++ b/apps/api/src/features/entry/schema/entry.schema.ts @@ -1,38 +1,38 @@ import { Users } from "@/features/user/schema"; import { - boolean, - integer, - json, - pgTable, - timestamp, - uuid, - varchar, + boolean, + integer, + json, + pgTable, + timestamp, + uuid, + varchar, } from "drizzle-orm/pg-core"; type TDocumentContent = { - type: "doc" | string; - content: Record[]; + type: "doc" | string; + content: Record[]; }; export const Entry = pgTable("entry", { - id: uuid("id").primaryKey().defaultRandom(), - user_id: uuid("user_id") - .references(() => Users.id, { onDelete: "cascade", onUpdate: "cascade" }) - .notNull(), - title: varchar("title", { length: 80 }).default("Untintled").notNull(), - created_at: timestamp("created_at").defaultNow().notNull(), - updated_at: timestamp("updated_at").defaultNow().notNull(), - word_count: integer("word_count").default(0).notNull(), - content: json("content") - .default({ - type: "doc", - content: [ - { - type: "paragraph", - }, - ], - }) - .$type(), - is_private: boolean("is_private").default(false).notNull(), - end_date: timestamp("end_date"), + id: uuid("id").primaryKey().defaultRandom(), + user_id: uuid("user_id") + .references(() => Users.id, { onDelete: "cascade", onUpdate: "cascade" }) + .notNull(), + title: varchar("title", { length: 80 }).default("Untintled").notNull(), + created_at: timestamp("created_at").defaultNow().notNull(), + updated_at: timestamp("updated_at").defaultNow().notNull(), + word_count: integer("word_count").default(0).notNull(), + content: json("content") + .default({ + type: "doc", + content: [ + { + type: "paragraph", + }, + ], + }) + .$type(), + is_private: boolean("is_private").default(false).notNull(), + end_date: timestamp("end_date"), }); diff --git a/apps/api/src/shared/plugins/cron/remove-entries/index.ts b/apps/api/src/shared/plugins/cron/remove-entries/index.ts index 35020be..fdcae0f 100644 --- a/apps/api/src/shared/plugins/cron/remove-entries/index.ts +++ b/apps/api/src/shared/plugins/cron/remove-entries/index.ts @@ -5,31 +5,31 @@ import { eq, isNotNull } from "drizzle-orm"; import type Elysia from "elysia"; export const pluginCronCleanDeletedEntries = (app: Elysia) => - app.use( - cron({ - pattern: "@daily", - name: "clean-deleted-entries", - async run(store) { - const deletedEntries = await db - .select() - .from(Entry) - .where(isNotNull(Entry.end_date)); - if (!deletedEntries.length || deletedEntries.length === 0) return; + app.use( + cron({ + pattern: "@daily", + name: "clean-deleted-entries", + async run(store) { + const deletedEntries = await db + .select() + .from(Entry) + .where(isNotNull(Entry.end_date)); + if (!deletedEntries.length || deletedEntries.length === 0) return; - const today = new Date(); + const today = new Date(); - for await (const deletedEntry of deletedEntries) { - if ( - deletedEntry.end_date !== null && - deletedEntry.end_date.getTime() < today.getTime() - ) { - try { - await db.delete(Entry).where(eq(Entry.id, deletedEntry.id)); - } catch (error) { - console.log("could not delete entry with id:", deletedEntry.id); - } - } - } - }, - }), - ); + for await (const deletedEntry of deletedEntries) { + if ( + deletedEntry.end_date !== null && + deletedEntry.end_date.getTime() < today.getTime() + ) { + try { + await db.delete(Entry).where(eq(Entry.id, deletedEntry.id)); + } catch (error) { + console.log("could not delete entry with id:", deletedEntry.id); + } + } + } + }, + }), + ); diff --git a/apps/api/src/shared/plugins/cron/remove-users/index.ts b/apps/api/src/shared/plugins/cron/remove-users/index.ts index 3ae1848..671bb21 100644 --- a/apps/api/src/shared/plugins/cron/remove-users/index.ts +++ b/apps/api/src/shared/plugins/cron/remove-users/index.ts @@ -5,31 +5,31 @@ import { eq, isNotNull } from "drizzle-orm"; import type { Elysia } from "elysia"; export const pluginCronCleanInactiveUsers = (app: Elysia) => { - return app.use( - cron({ - name: "clean-inactive-users", - pattern: "@daily", - async run(cronStore) { - const inactiveUsers = await db - .select() - .from(Users) - .where(isNotNull(Users.end_date)); + return app.use( + cron({ + name: "clean-inactive-users", + pattern: "@daily", + async run(cronStore) { + const inactiveUsers = await db + .select() + .from(Users) + .where(isNotNull(Users.end_date)); - if (!inactiveUsers.length || inactiveUsers.length === 0) return; + if (!inactiveUsers.length || inactiveUsers.length === 0) return; - const today = new Date(); - for await (const inactiveUser of inactiveUsers) { - if ( - inactiveUser.end_date !== null && - today.getTime() <= new Date(inactiveUser.end_date).getTime() - ) - try { - await db.delete(Users).where(eq(Users.id, inactiveUser.id)); - } catch (error) { - console.log("could not delete user with id:", inactiveUser.id); - } - } - }, - }), - ); + const today = new Date(); + for await (const inactiveUser of inactiveUsers) { + if ( + inactiveUser.end_date !== null && + today.getTime() <= new Date(inactiveUser.end_date).getTime() + ) + try { + await db.delete(Users).where(eq(Users.id, inactiveUser.id)); + } catch (error) { + console.log("could not delete user with id:", inactiveUser.id); + } + } + }, + }), + ); }; diff --git a/apps/api/tests/integration/entry/delete.test.ts b/apps/api/tests/integration/entry/delete.test.ts index 6cb9c31..37f8885 100644 --- a/apps/api/tests/integration/entry/delete.test.ts +++ b/apps/api/tests/integration/entry/delete.test.ts @@ -6,81 +6,81 @@ import { createUser } from "@/tests/lib/user"; import { endpointPath } from "."; describe("Test DELETE method on entries endpoints", () => { - describe("Delete own entry succesfully", async () => { - const { cookie } = await createUser({}); - - const { EntryId } = await createNewEntry( - { title: "test", content: EXAMPLE_DOCUMENT_CONTENT, word_count: 0 }, - cookie, - ); - - if (!EntryId) throw new Error("Entry not created"); - - const res = await app.handle( - new Request(`${endpointPath}/${EntryId}`, { - method: "DELETE", - headers: { - "Content-Type": "application/json; charset=utf-8", - cookie: cookie, - }, - }), - ); - - it("Should return status 202", () => { - expect(res.status).toBe(202); - }); - - it("Should return empty object on body response", async () => { - const body = await res.json(); - - expect(body).toBeEmptyObject(); - }); - }); - - test.todo("Delete entry not ownd by user", async () => { - it("Should return status 401", () => {}); - - it("Should return empty object on body response", async () => {}); - }); - - describe("should not return deleted entry after delete", async () => { - const { cookie } = await createUser({}); - - const { EntryId } = await createNewEntry( - { title: "test", content: EXAMPLE_DOCUMENT_CONTENT, word_count: 0 }, - cookie, - ); - - if (!EntryId) throw new Error("entry not created"); - - await app.handle( - new Request(`${endpointPath}/${EntryId}`, { - method: "DELETE", - headers: { - "Content-Type": "application/json; charset=utf-8", - cookie: cookie, - }, - }), - ); - - const res = await app.handle( - new Request(`${endpointPath}/${EntryId}`, { - method: "GET", - headers: { - "Content-Type": "application/json; charset=utf-8", - cookie: cookie, - }, - }), - ); - - it("should not return 200", () => { - expect(res.status).not.toBe(200); - }); - - it("should not return entry", async () => { - const body = await res.json(); - - expect(body).toBeEmptyObject(); - }); - }); + describe("Delete own entry succesfully", async () => { + const { cookie } = await createUser({}); + + const { EntryId } = await createNewEntry( + { title: "test", content: EXAMPLE_DOCUMENT_CONTENT, word_count: 0 }, + cookie, + ); + + if (!EntryId) throw new Error("Entry not created"); + + const res = await app.handle( + new Request(`${endpointPath}/${EntryId}`, { + method: "DELETE", + headers: { + "Content-Type": "application/json; charset=utf-8", + cookie: cookie, + }, + }), + ); + + it("Should return status 202", () => { + expect(res.status).toBe(202); + }); + + it("Should return empty object on body response", async () => { + const body = await res.json(); + + expect(body).toBeEmptyObject(); + }); + }); + + test.todo("Delete entry not ownd by user", async () => { + it("Should return status 401", () => {}); + + it("Should return empty object on body response", async () => {}); + }); + + describe("should not return deleted entry after delete", async () => { + const { cookie } = await createUser({}); + + const { EntryId } = await createNewEntry( + { title: "test", content: EXAMPLE_DOCUMENT_CONTENT, word_count: 0 }, + cookie, + ); + + if (!EntryId) throw new Error("entry not created"); + + await app.handle( + new Request(`${endpointPath}/${EntryId}`, { + method: "DELETE", + headers: { + "Content-Type": "application/json; charset=utf-8", + cookie: cookie, + }, + }), + ); + + const res = await app.handle( + new Request(`${endpointPath}/${EntryId}`, { + method: "GET", + headers: { + "Content-Type": "application/json; charset=utf-8", + cookie: cookie, + }, + }), + ); + + it("should not return 200", () => { + expect(res.status).not.toBe(200); + }); + + it("should not return entry", async () => { + const body = await res.json(); + + expect(body).toBeEmptyObject(); + }); + }); }); diff --git a/apps/api/tests/integration/entry/get.test.ts b/apps/api/tests/integration/entry/get.test.ts index 6a72b63..1b03dca 100644 --- a/apps/api/tests/integration/entry/get.test.ts +++ b/apps/api/tests/integration/entry/get.test.ts @@ -3,8 +3,8 @@ import { Value } from "@sinclair/typebox/value"; import { describe, expect, it, test } from "bun:test"; import { app } from "@/app"; import { - EntrySafeSchema, - type TEntrySafe, + EntrySafeSchema, + type TEntrySafe, } from "@/features/entry/models/entry.models"; import { EXAMPLE_DOCUMENT_CONTENT } from "@/tests/lib/constants"; import { createNewEntry } from "@/tests/lib/entry"; @@ -12,109 +12,109 @@ import { createUser } from "@/tests/lib/user"; import { endpointPath } from "."; describe("Test GET method on entries endpoints", () => { - describe("GET private user entries", () => { - describe("GET entry list of user", async () => { - const { cookie } = await createUser({}); - - await createNewEntry( - { - word_count: 0, - title: "test entry 1", - content: EXAMPLE_DOCUMENT_CONTENT, - }, - cookie, - ); - - await createNewEntry( - { - word_count: 0, - title: "test entry 2", - content: EXAMPLE_DOCUMENT_CONTENT, - }, - cookie, - ); - - await createNewEntry( - { - word_count: 0, - title: "test entry 3", - content: EXAMPLE_DOCUMENT_CONTENT, - }, - cookie, - ); - - const res = await app.handle( - new Request(`${endpointPath}/`, { - method: "GET", - headers: { - "Content-Type": "application/json; charset=utf-8", - cookie: cookie, - }, - }), - ); - - it("Should return status code 200", () => { - expect(res.status).toBe(200); - }); - - describe("test response body", async () => { - const body: TEntrySafe[] = await res.json(); - - it("Should return object on body", () => { - expect(body).toBeObject(); - }); - - it("Should return entry list", () => { - const areEntriesValid = body.every((entry) => - Value.Check(EntrySafeSchema, entry), - ); - - expect(areEntriesValid).toBeTrue(); - }); - }); - }); - describe("GET entry lists by title", async () => {}); - describe("GET entry by entryId", async () => { - const { cookie } = await createUser({}); - - const { EntryId } = await createNewEntry( - { title: "Untitled", content: EXAMPLE_DOCUMENT_CONTENT, word_count: 8 }, - cookie, - ); - - const res = await app.handle( - new Request(`${endpointPath}/${EntryId}`, { - method: "GET", - headers: { - "Content-Type": "application/json; charset=utf-8", - cookie: cookie, - }, - }), - ); - - it("should return 200 status code", () => { - expect(res.status).toBe(200); - }); - - it("should return entry on body", async () => { - const body = await res.json(); - - const isBodyAValidEntry = Value.Check(EntrySafeSchema, body); - - expect(isBodyAValidEntry).toBeTrue(); - }); - }); - }); - - test.todo("GET public entries", () => { - describe("GET public entries", () => { - describe("GET public entry by entryId", async () => {}); - }); - }); - - describe("GET private entry not owned by user", async () => { - it("Should return 401", () => {}); - - it("Should return empty object on body response", () => {}); - }); + describe("GET private user entries", () => { + describe("GET entry list of user", async () => { + const { cookie } = await createUser({}); + + await createNewEntry( + { + word_count: 0, + title: "test entry 1", + content: EXAMPLE_DOCUMENT_CONTENT, + }, + cookie, + ); + + await createNewEntry( + { + word_count: 0, + title: "test entry 2", + content: EXAMPLE_DOCUMENT_CONTENT, + }, + cookie, + ); + + await createNewEntry( + { + word_count: 0, + title: "test entry 3", + content: EXAMPLE_DOCUMENT_CONTENT, + }, + cookie, + ); + + const res = await app.handle( + new Request(`${endpointPath}/`, { + method: "GET", + headers: { + "Content-Type": "application/json; charset=utf-8", + cookie: cookie, + }, + }), + ); + + it("Should return status code 200", () => { + expect(res.status).toBe(200); + }); + + describe("test response body", async () => { + const body: TEntrySafe[] = await res.json(); + + it("Should return object on body", () => { + expect(body).toBeObject(); + }); + + it("Should return entry list", () => { + const areEntriesValid = body.every((entry) => + Value.Check(EntrySafeSchema, entry), + ); + + expect(areEntriesValid).toBeTrue(); + }); + }); + }); + describe("GET entry lists by title", async () => {}); + describe("GET entry by entryId", async () => { + const { cookie } = await createUser({}); + + const { EntryId } = await createNewEntry( + { title: "Untitled", content: EXAMPLE_DOCUMENT_CONTENT, word_count: 8 }, + cookie, + ); + + const res = await app.handle( + new Request(`${endpointPath}/${EntryId}`, { + method: "GET", + headers: { + "Content-Type": "application/json; charset=utf-8", + cookie: cookie, + }, + }), + ); + + it("should return 200 status code", () => { + expect(res.status).toBe(200); + }); + + it("should return entry on body", async () => { + const body = await res.json(); + + const isBodyAValidEntry = Value.Check(EntrySafeSchema, body); + + expect(isBodyAValidEntry).toBeTrue(); + }); + }); + }); + + test.todo("GET public entries", () => { + describe("GET public entries", () => { + describe("GET public entry by entryId", async () => {}); + }); + }); + + describe("GET private entry not owned by user", async () => { + it("Should return 401", () => {}); + + it("Should return empty object on body response", () => {}); + }); }); diff --git a/apps/api/tests/integration/entry/guard.test.ts b/apps/api/tests/integration/entry/guard.test.ts index 6b7457e..a977b7d 100644 --- a/apps/api/tests/integration/entry/guard.test.ts +++ b/apps/api/tests/integration/entry/guard.test.ts @@ -7,124 +7,124 @@ import { createUser } from "@/tests/lib/user"; import { endpointPath } from "."; describe("Entry guard", () => { - describe("Unauthenticated user should not be able to perform requests to endpoint", () => { - test.todo("Should not be able to perform PATCH", async () => { - const res = await app.handle( - new Request(`${endpointPath}`, { - method: "PATCH", - headers: { - "Content-Type": "application/json; charset=utf-8", - Cookie: "not-a-cookie", - }, - body: JSON.stringify({ - title: "test", - content: EXAMPLE_DOCUMENT_CONTENT, - word_count: 291, - }), - }), - ); - - it("should return 401 status code", async () => { - expect(res.status).toBe(401); - }); - - it("should return empty object ton body response", async () => { - const body = await res.json(); - - expect(body).toBeEmptyObject(); - }); - }); - - describe("Should not be able to perform GET", async () => { - const { cookie } = await createUser({}); - - await createNewEntry( - { - word_count: 0, - title: "test entry 3", - content: EXAMPLE_DOCUMENT_CONTENT, - }, - cookie, - ); - - const res = await app.handle( - new Request(`${endpointPath}`, { - method: "GET", - headers: { - "Content-Type": "application/json; charset=utf-8", - Cookie: "not-a-cookie", - }, - }), - ); - - it("should return 401 status code", async () => { - expect(res.status).toBe(401); - }); - - it("should return empty object ton body response", async () => { - const body = await res.json(); - expect(body).toBeEmptyObject(); - }); - }); - describe("Should not be able to perform DELETE", async () => { - const { cookie } = await createUser({}); - - const { EntryId } = await createNewEntry( - { title: "test", content: EXAMPLE_DOCUMENT_CONTENT, word_count: 0 }, - cookie, - ); - - if (!EntryId) throw new Error("entry not created"); - - const res = await app.handle( - new Request(`${endpointPath}/${EntryId}`, { - method: "DELETE", - headers: { - "Content-Type": "application/json; charset=utf-8", - cookie: "not-a-cookie", - }, - }), - ); - - it("should return 401 status code", async () => { - expect(res.status).toBe(401); - }); - - it("should return empty object ton body response", async () => { - const body = await res.json(); - expect(body).toBeEmptyObject(); - }); - }); - describe("Should not be able to perform POST", async () => { - const res = await app.handle( - new Request(`${endpointPath}`, { - method: "POST", - headers: { - "Content-Type": "application/json; charset=utf-8", - cookie: "not-a-cookie", - }, - body: JSON.stringify({ - title: "test", - content: EXAMPLE_DOCUMENT_CONTENT, - word_count: 291, - }), - }), - ); - - it("should return 401 status code", async () => { - expect(res.status).toBe(401); - }); - - it("should return empty object ton body response", async () => { - const body = await res.json(); - - expect(body).toBeEmptyObject(); - }); - }); - }); - - test.todo( - "User should not be able to get private entry of other user", - async () => {}, - ); + describe("Unauthenticated user should not be able to perform requests to endpoint", () => { + test.todo("Should not be able to perform PATCH", async () => { + const res = await app.handle( + new Request(`${endpointPath}`, { + method: "PATCH", + headers: { + "Content-Type": "application/json; charset=utf-8", + Cookie: "not-a-cookie", + }, + body: JSON.stringify({ + title: "test", + content: EXAMPLE_DOCUMENT_CONTENT, + word_count: 291, + }), + }), + ); + + it("should return 401 status code", async () => { + expect(res.status).toBe(401); + }); + + it("should return empty object ton body response", async () => { + const body = await res.json(); + + expect(body).toBeEmptyObject(); + }); + }); + + describe("Should not be able to perform GET", async () => { + const { cookie } = await createUser({}); + + await createNewEntry( + { + word_count: 0, + title: "test entry 3", + content: EXAMPLE_DOCUMENT_CONTENT, + }, + cookie, + ); + + const res = await app.handle( + new Request(`${endpointPath}`, { + method: "GET", + headers: { + "Content-Type": "application/json; charset=utf-8", + Cookie: "not-a-cookie", + }, + }), + ); + + it("should return 401 status code", async () => { + expect(res.status).toBe(401); + }); + + it("should return empty object ton body response", async () => { + const body = await res.json(); + expect(body).toBeEmptyObject(); + }); + }); + describe("Should not be able to perform DELETE", async () => { + const { cookie } = await createUser({}); + + const { EntryId } = await createNewEntry( + { title: "test", content: EXAMPLE_DOCUMENT_CONTENT, word_count: 0 }, + cookie, + ); + + if (!EntryId) throw new Error("entry not created"); + + const res = await app.handle( + new Request(`${endpointPath}/${EntryId}`, { + method: "DELETE", + headers: { + "Content-Type": "application/json; charset=utf-8", + cookie: "not-a-cookie", + }, + }), + ); + + it("should return 401 status code", async () => { + expect(res.status).toBe(401); + }); + + it("should return empty object ton body response", async () => { + const body = await res.json(); + expect(body).toBeEmptyObject(); + }); + }); + describe("Should not be able to perform POST", async () => { + const res = await app.handle( + new Request(`${endpointPath}`, { + method: "POST", + headers: { + "Content-Type": "application/json; charset=utf-8", + cookie: "not-a-cookie", + }, + body: JSON.stringify({ + title: "test", + content: EXAMPLE_DOCUMENT_CONTENT, + word_count: 291, + }), + }), + ); + + it("should return 401 status code", async () => { + expect(res.status).toBe(401); + }); + + it("should return empty object ton body response", async () => { + const body = await res.json(); + + expect(body).toBeEmptyObject(); + }); + }); + }); + + test.todo( + "User should not be able to get private entry of other user", + async () => {}, + ); }); diff --git a/apps/api/tests/integration/entry/patch.test.ts b/apps/api/tests/integration/entry/patch.test.ts index 8514dc6..2c94b58 100644 --- a/apps/api/tests/integration/entry/patch.test.ts +++ b/apps/api/tests/integration/entry/patch.test.ts @@ -6,37 +6,37 @@ import { createUser } from "@/tests/lib/user"; import { endpointPath } from "."; describe("Test PATCH method on entries endpoints", () => { - describe("Succesfull request", async () => { - const { cookie } = await createUser({}); + describe("Succesfull request", async () => { + const { cookie } = await createUser({}); - const { EntryId } = await createNewEntry( - { title: "untitled", content: EXAMPLE_DOCUMENT_CONTENT, word_count: 0 }, - cookie, - ); + const { EntryId } = await createNewEntry( + { title: "untitled", content: EXAMPLE_DOCUMENT_CONTENT, word_count: 0 }, + cookie, + ); - const res = await app.handle( - new Request(`${endpointPath}/${EntryId}`, { - method: "PATCH", - headers: { - "Content-Type": "application/json; charset=utf-8", - cookie: cookie, - }, - body: JSON.stringify({ - title: "test", - content: EXAMPLE_DOCUMENT_CONTENT, - word_count: 291, - }), - }), - ); + const res = await app.handle( + new Request(`${endpointPath}/${EntryId}`, { + method: "PATCH", + headers: { + "Content-Type": "application/json; charset=utf-8", + cookie: cookie, + }, + body: JSON.stringify({ + title: "test", + content: EXAMPLE_DOCUMENT_CONTENT, + word_count: 291, + }), + }), + ); - it("Should return status code 201", () => { - expect(res.status).toBe(201); - }); + it("Should return status code 201", () => { + expect(res.status).toBe(201); + }); - it("Should return empty object on body", async () => { - const body = await res.json(); + it("Should return empty object on body", async () => { + const body = await res.json(); - expect(body).toBeEmptyObject(); - }); - }); + expect(body).toBeEmptyObject(); + }); + }); }); diff --git a/apps/api/tests/integration/entry/post.test.ts b/apps/api/tests/integration/entry/post.test.ts index 253c68e..05537ec 100644 --- a/apps/api/tests/integration/entry/post.test.ts +++ b/apps/api/tests/integration/entry/post.test.ts @@ -5,68 +5,68 @@ import { createUser } from "@/tests/lib/user"; import { endpointPath } from "."; describe("Test POST method on entries endpoints", () => { - describe("Entry created succesfully", async () => { - const { cookie } = await createUser({}); + describe("Entry created succesfully", async () => { + const { cookie } = await createUser({}); - const res = await app.handle( - new Request(`${endpointPath}`, { - method: "POST", - headers: { - "Content-Type": "application/json; charset=utf-8", - cookie: cookie, - }, - body: JSON.stringify({ - title: "test", - content: EXAMPLE_DOCUMENT_CONTENT, - word_count: 291, - }), - }), - ); + const res = await app.handle( + new Request(`${endpointPath}`, { + method: "POST", + headers: { + "Content-Type": "application/json; charset=utf-8", + cookie: cookie, + }, + body: JSON.stringify({ + title: "test", + content: EXAMPLE_DOCUMENT_CONTENT, + word_count: 291, + }), + }), + ); - it("should return 201 status code", async () => { - expect(res.status).toBe(201); - }); + it("should return 201 status code", async () => { + expect(res.status).toBe(201); + }); - describe("return body", async () => { - const body: { id: string } = await res.json(); + describe("return body", async () => { + const body: { id: string } = await res.json(); - it("should return object on body response", () => { - expect(body).toBeObject(); - }); + it("should return object on body response", () => { + expect(body).toBeObject(); + }); - it("should return new entry id on body response", () => { - expect(body).toContainKey("id"); - expect(body.id).toBeString(); - }); - }); - }); + it("should return new entry id on body response", () => { + expect(body).toContainKey("id"); + expect(body.id).toBeString(); + }); + }); + }); - describe("Incorrect schema on body json", async () => { - const { cookie } = await createUser({}); + describe("Incorrect schema on body json", async () => { + const { cookie } = await createUser({}); - const res = await app.handle( - new Request(`${endpointPath}/`, { - method: "POST", - headers: { - "Content-Type": "application/json; charset=utf-8", - cookie: cookie, - }, - body: JSON.stringify({ - title: 1233, - content: "asdfas", - word_count: "asdf", - }), - }), - ); + const res = await app.handle( + new Request(`${endpointPath}/`, { + method: "POST", + headers: { + "Content-Type": "application/json; charset=utf-8", + cookie: cookie, + }, + body: JSON.stringify({ + title: 1233, + content: "asdfas", + word_count: "asdf", + }), + }), + ); - it("should return 400 status code", async () => { - expect(res.status).toBe(400); - }); + it("should return 400 status code", async () => { + expect(res.status).toBe(400); + }); - it("should return error message", async () => { - const body = await res.json(); + it("should return error message", async () => { + const body = await res.json(); - expect(body).toContainKey("error"); - }); - }); + expect(body).toContainKey("error"); + }); + }); }); diff --git a/apps/api/tests/lib/constants/index.ts b/apps/api/tests/lib/constants/index.ts index c8dc78d..e684329 100644 --- a/apps/api/tests/lib/constants/index.ts +++ b/apps/api/tests/lib/constants/index.ts @@ -1,12 +1,12 @@ import type { TInsertEntry } from "@/features/entry/models/entry.models"; export const EXAMPLE_DOCUMENT_CONTENT: TInsertEntry["content"] = { - type: "doc", - content: [ - { - type: "heading", - attrs: { level: 1 }, - content: [{ type: "text", marks: [{ type: "bold" }], text: "Memoir" }], - }, - ], + type: "doc", + content: [ + { + type: "heading", + attrs: { level: 1 }, + content: [{ type: "text", marks: [{ type: "bold" }], text: "Memoir" }], + }, + ], }; diff --git a/apps/api/tests/lib/entry/index.ts b/apps/api/tests/lib/entry/index.ts index 0e1e851..f54154f 100644 --- a/apps/api/tests/lib/entry/index.ts +++ b/apps/api/tests/lib/entry/index.ts @@ -3,25 +3,25 @@ import type { TInsertEntry } from "@/features/entry/models/entry.models"; import { endpointPath } from "@/tests/integration/entry"; export async function createNewEntry( - entry: TInsertEntry, - cookie: string, + entry: TInsertEntry, + cookie: string, ): Promise<{ EntryId: string | null }> { - try { - const res = await app.handle( - new Request(`${endpointPath}/`, { - method: "POST", - headers: { - "Content-Type": "application/json; charset=utf-8", - cookie: cookie, - }, - body: JSON.stringify(entry), - }), - ); + try { + const res = await app.handle( + new Request(`${endpointPath}/`, { + method: "POST", + headers: { + "Content-Type": "application/json; charset=utf-8", + cookie: cookie, + }, + body: JSON.stringify(entry), + }), + ); - const body: { id: string } = await res.json(); + const body: { id: string } = await res.json(); - return { EntryId: body.id }; - } catch (error) { - return { EntryId: null }; - } + return { EntryId: body.id }; + } catch (error) { + return { EntryId: null }; + } }