From d043b5aed7313c24e857989c57293aedfba6c495 Mon Sep 17 00:00:00 2001 From: Akhil Reddy Date: Fri, 7 Feb 2025 18:39:55 +0530 Subject: [PATCH] add notes routes --- src/note/note.controller.ts | 79 +++++++++++++++++++++++++++++++++++ src/note/note.route.ts | 79 +++++++++++++++++++++++++++++++++++ src/note/note.schema.ts | 43 +++++++++++++++++++ src/note/note.service.ts | 83 +++++++++++++++++++++++++++++++++++++ src/permit/permit.route.ts | 12 ++++++ src/rts/rts.route.ts | 7 ++++ src/server.ts | 2 + src/task/task.route.ts | 7 ++++ 8 files changed, 312 insertions(+) create mode 100644 src/note/note.controller.ts create mode 100644 src/note/note.route.ts create mode 100644 src/note/note.schema.ts create mode 100644 src/note/note.service.ts diff --git a/src/note/note.controller.ts b/src/note/note.controller.ts new file mode 100644 index 0000000..3ba778f --- /dev/null +++ b/src/note/note.controller.ts @@ -0,0 +1,79 @@ +import { FastifyReply, FastifyRequest } from "fastify"; +import { createNote, deleteNote, listNotes, updateNote } from "./note.service"; +import { CreateNoteInput } from "./note.schema"; + +export async function createNoteHandler( + req: FastifyRequest, + res: FastifyReply +) { + const { resourceId } = req.params as { resourceId: string }; + const input = req.body as CreateNoteInput; + + try { + const note = await createNote(input, resourceId, req.user); + return res.code(201).send(note); + } catch (err) { + return err; + } +} + +export async function listNotesHandler(req: FastifyRequest, res: FastifyReply) { + const { resourceId } = req.params as { resourceId: string }; + + try { + const notes = await listNotes(resourceId, req.user.tenantId); + return res.code(200).send({ notes: notes }); + } catch (err) { + return err; + } +} + +export async function updateNoteHandler( + req: FastifyRequest, + res: FastifyReply +) { + const { resourceId, noteId } = req.params as { + resourceId: string; + noteId: string; + }; + + const input = req.body as CreateNoteInput; + + try { + const updatedNote = await updateNote( + input, + resourceId, + noteId, + req.user.tenantId + ); + if (!updateNote) return res.code(404).send({ error: "resource not found" }); + + return res.code(200).send(updatedNote); + } catch (err) { + return err; + } +} + +export async function deleteNoteHandler( + req: FastifyRequest, + res: FastifyReply +) { + const { resourceId, noteId } = req.params as { + resourceId: string; + noteId: string; + }; + + try { + const deleteResult = await deleteNote( + resourceId, + noteId, + req.user.tenantId + ); + if (deleteResult.deletedCount == 0) + return res.code(404).send({ error: "resource not found" }); + + return res.code(204).send(); + } catch (err) { + return err; + } +} diff --git a/src/note/note.route.ts b/src/note/note.route.ts new file mode 100644 index 0000000..39d1fa6 --- /dev/null +++ b/src/note/note.route.ts @@ -0,0 +1,79 @@ +import { FastifyInstance } from "fastify"; +import { + createNoteHandler, + listNotesHandler, + deleteNoteHandler, + updateNoteHandler, +} from "./note.controller"; +import { Claim } from "../utils/claims"; + +export async function noteRoutes( + fastify: FastifyInstance, + claims: { read: Claim; write: Claim; delete: Claim } +) { + fastify.post( + "/:resourceId/notes", + { + schema: { + params: { + type: "object", + properties: { resourceId: { type: "string" } }, + }, + }, + config: { requiredClaims: [claims.write] }, + preHandler: [fastify.authorize], + }, + createNoteHandler + ); + + fastify.get( + "/:resourceId/notes", + { + schema: { + params: { + type: "object", + properties: { resourceId: { type: "string" } }, + }, + }, + config: { requiredClaims: [claims.read] }, + preHandler: [fastify.authorize], + }, + listNotesHandler + ); + + fastify.patch( + "/:resourceId/notes/:noteId", + { + schema: { + params: { + type: "object", + properties: { + resourceId: { type: "string" }, + noteId: { type: "string" }, + }, + }, + }, + config: { requiredClaims: [claims.write] }, + preHandler: [fastify.authorize], + }, + updateNoteHandler + ); + + fastify.delete( + "/:resourceId/notes/:noteId", + { + schema: { + params: { + type: "object", + properties: { + resourceId: { type: "string" }, + noteId: { type: "string" }, + }, + }, + }, + config: { requiredClaims: [claims.write] }, + preHandler: [fastify.authorize], + }, + deleteNoteHandler + ); +} diff --git a/src/note/note.schema.ts b/src/note/note.schema.ts new file mode 100644 index 0000000..fecd78a --- /dev/null +++ b/src/note/note.schema.ts @@ -0,0 +1,43 @@ +import { buildJsonSchemas } from "fastify-zod"; +import mongoose from "mongoose"; +import { z } from "zod"; + +const noteSchema = new mongoose.Schema({ + tenantId: { + type: String, + required: true, + }, + pid: { + type: String, + unique: true, + required: true, + }, + resourceId: { + type: String, + required: true, + }, + content: { + type: String, + required: true, + }, + createdAt: Date, + createdBy: { + type: mongoose.Types.ObjectId, + ref: "user", + }, +}); + +export const noteModel = mongoose.model("note", noteSchema); + +const createNoteInput = z.object({ + content: z.string(), +}); + +export type CreateNoteInput = z.infer; + +export const { schemas: noteSchemas, $ref: $note } = buildJsonSchemas( + { + createNoteInput, + }, + { $id: "note" } +); diff --git a/src/note/note.service.ts b/src/note/note.service.ts new file mode 100644 index 0000000..5904bf3 --- /dev/null +++ b/src/note/note.service.ts @@ -0,0 +1,83 @@ +import { AuthenticatedUser } from "../auth"; +import { generateId } from "../utils/id"; +import { CreateNoteInput, noteModel } from "./note.schema"; + +export async function createNote( + input: CreateNoteInput, + resourceId: string, + user: AuthenticatedUser +) { + return await noteModel.create({ + tenantId: user.tenantId, + pid: generateId(), + resourceId: resourceId, + content: input.content, + createdAt: new Date(), + createdBy: user.userId, + }); +} + +export async function updateNote( + input: CreateNoteInput, + resourceId: string, + noteId: string, + tenantId: string +) { + return await noteModel.findOneAndUpdate( + { + $and: [ + { pid: noteId }, + { tenantId: tenantId }, + { resourceId: resourceId }, + ], + }, + { ...input }, + { new: true } + ); +} + +export async function listNotes(resourceId: string, tenantId: string) { + return await noteModel.aggregate([ + { + $match: { $and: [{ resourceId: resourceId }, { tenantId: tenantId }] }, + }, + { + $lookup: { + from: "users", + localField: "createdBy", + foreignField: "_id", + as: "user", + }, + }, + { + $project: { + _id: 1, + pid: 1, + resourceId: 1, + content: 1, + createdAt: 1, + createdBy: { + $let: { + vars: { user: { $arrayElemAt: ["$user", 0] } }, + in: { + _id: "$$user._id", + pid: "$$user.pid", + name: "$$user.name", + avatar: "$$user.avatar", + }, + }, + }, + }, + }, + ]); +} + +export async function deleteNote( + resourceId: string, + noteId: string, + tenantId: string +) { + return await noteModel.deleteOne({ + $and: [{ pid: noteId }, { tenantId: tenantId }, { resourceId: resourceId }], + }); +} diff --git a/src/permit/permit.route.ts b/src/permit/permit.route.ts index 668e690..c426716 100644 --- a/src/permit/permit.route.ts +++ b/src/permit/permit.route.ts @@ -10,6 +10,12 @@ import { } from "./permit.controller"; import { $permit } from "./permit.schema"; import { hideFields } from "../auth"; +import { + createNoteHandler, + deleteNoteHandler, + listNotesHandler, +} from "../note/note.controller"; +import { noteRoutes } from "../note/note.route"; export async function permitRoutes(fastify: FastifyInstance) { fastify.post( @@ -111,5 +117,11 @@ export async function permitRoutes(fastify: FastifyInstance) { getUniqueFieldValuesPermit ); + await noteRoutes(fastify, { + read: "permit:read", + write: "permit:write", + delete: "permit:delete", + }); + fastify.addHook("onSend", hideFields("permits")); } diff --git a/src/rts/rts.route.ts b/src/rts/rts.route.ts index 89f40d2..0fc9bbd 100644 --- a/src/rts/rts.route.ts +++ b/src/rts/rts.route.ts @@ -9,6 +9,7 @@ import { updateRtsHandler, } from "./rts.controller"; import { hideFields } from "../auth"; +import { noteRoutes } from "../note/note.route"; export async function rtsRoutes(fastify: FastifyInstance) { fastify.post( @@ -94,5 +95,11 @@ export async function rtsRoutes(fastify: FastifyInstance) { newFilesHandler ); + await noteRoutes(fastify, { + read: "rts:read", + write: "rts:write", + delete: "rts:delete", + }); + fastify.addHook("onSend", hideFields("rts")); } diff --git a/src/server.ts b/src/server.ts index e1297ea..39c2b90 100644 --- a/src/server.ts +++ b/src/server.ts @@ -15,6 +15,7 @@ import { authRoutes } from "./auth/auth.route"; import { rtsSchemas } from "./rts/rts.schema"; import { taskSchemas } from "./task/task.schema"; import { notificationSchemas } from "./notification/notification.schema"; +import { noteSchemas } from "./note/note.schema"; const app = fastify({ logger: true }); @@ -43,6 +44,7 @@ for (const schema of [ ...rtsSchemas, ...taskSchemas, ...notificationSchemas, + ...noteSchemas, ]) { app.addSchema(schema); } diff --git a/src/task/task.route.ts b/src/task/task.route.ts index 64a9d98..d356005 100644 --- a/src/task/task.route.ts +++ b/src/task/task.route.ts @@ -7,6 +7,7 @@ import { listTaskHandler, updateTaskHandler, } from "./task.controller"; +import { noteRoutes } from "../note/note.route"; export async function taskRoutes(fastify: FastifyInstance) { fastify.post( @@ -79,4 +80,10 @@ export async function taskRoutes(fastify: FastifyInstance) { }, deleteTaskHandler ); + + await noteRoutes(fastify, { + read: "task:read", + write: "task:write", + delete: "task:delete", + }); }