From a8d336ca4eb34cd6e1455bb75e0a74cfc7c16b60 Mon Sep 17 00:00:00 2001 From: Akhil Meka Date: Tue, 4 Mar 2025 11:35:52 +0530 Subject: [PATCH] add view routes --- src/routes.ts | 2 + src/utils/claims.ts | 5 ++- src/utils/roles.ts | 3 ++ src/view/view.controller.ts | 84 +++++++++++++++++++++++++++++++++++ src/view/view.route.ts | 87 +++++++++++++++++++++++++++++++++++++ src/view/view.service.ts | 64 ++++++++++++++++++++++++--- 6 files changed, 239 insertions(+), 6 deletions(-) diff --git a/src/routes.ts b/src/routes.ts index 108944c..5e39bae 100644 --- a/src/routes.ts +++ b/src/routes.ts @@ -11,6 +11,7 @@ import { realTimeRoutes } from "./realtime/realtime.route"; import { notificationRoutes } from "./notification/notification.route"; import { configRoutes } from "./config/config.route"; import { mailProxyRoutes } from "./mailProxy/mailProxy.route"; +import { viewRoutes } from "./view/view.route"; export default async function routes(fastify: FastifyInstance) { fastify.addHook("preHandler", authHandler); @@ -24,5 +25,6 @@ export default async function routes(fastify: FastifyInstance) { fastify.register(notificationRoutes, { prefix: "/notifications" }); fastify.register(mailProxyRoutes, { prefix: "/proxy" }); fastify.register(configRoutes, { prefix: "/config" }); + fastify.register(viewRoutes, { prefix: "/views" }); fastify.register(realTimeRoutes); } diff --git a/src/utils/claims.ts b/src/utils/claims.ts index 96faf9c..5101770 100644 --- a/src/utils/claims.ts +++ b/src/utils/claims.ts @@ -24,4 +24,7 @@ export type Claim = | "notification:write" | "config:read" | "config:write" - | "mail:all"; + | "mail:all" + | "view:read" + | "view:write" + | "view:delete"; diff --git a/src/utils/roles.ts b/src/utils/roles.ts index c73b94e..75c863a 100644 --- a/src/utils/roles.ts +++ b/src/utils/roles.ts @@ -29,6 +29,9 @@ export const rules: Record< "config:read", "config:write", "mail:all", + "view:read", + "view:write", + "view:delete", ], hiddenFields: { orgs: ["__v"], diff --git a/src/view/view.controller.ts b/src/view/view.controller.ts index e69de29..c2a917c 100644 --- a/src/view/view.controller.ts +++ b/src/view/view.controller.ts @@ -0,0 +1,84 @@ +import { FastifyReply, FastifyRequest } from "fastify"; +import { CreateViewInput, UpdateViewInput } from "./view.schema"; +import { + createView, + deleteView, + getView, + listViews, + updateView, +} from "./view.service"; +import { PageQueryParams } from "../pagination"; + +export async function createViewHandler( + req: FastifyRequest, + res: FastifyReply +) { + const input = req.body as CreateViewInput; + + try { + const view = await createView(input, req.user); + return res.code(201).send(view); + } catch (err) { + return err; + } +} + +export async function getViewHandler(req: FastifyRequest, res: FastifyReply) { + const { viewId } = req.params as { viewId: string }; + + try { + const view = await getView(viewId, req.user.tenantId); + if (view === null) + return res.code(404).send({ error: "resource not found" }); + + return res.code(200).send(view); + } catch (err) { + return err; + } +} + +export async function listViewHandler(req: FastifyRequest, res: FastifyReply) { + const params = req.query as PageQueryParams; + + try { + const views = await listViews(params, req.user.tenantId); + return res.code(200).send(views); + } catch (err) { + return err; + } +} + +export async function updateViewHandler( + req: FastifyRequest, + res: FastifyReply +) { + const input = req.body as UpdateViewInput; + const { viewId } = req.params as { viewId: string }; + + try { + const updatedView = await updateView(viewId, input, req.user.tenantId); + if (!updatedView) + return res.code(404).send({ error: "resource not found" }); + + return res.code(200).send(updateView); + } catch (err) { + return err; + } +} + +export async function deleteViewHandler( + req: FastifyRequest, + res: FastifyReply +) { + const { viewId } = req.params as { viewId: string }; + + try { + const deleteResult = await deleteView(viewId); + 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/view/view.route.ts b/src/view/view.route.ts index e69de29..bcd70d9 100644 --- a/src/view/view.route.ts +++ b/src/view/view.route.ts @@ -0,0 +1,87 @@ +import { FastifyInstance } from "fastify"; +import { + createViewHandler, + deleteViewHandler, + getViewHandler, + listViewHandler, + updateViewHandler, +} from "./view.controller"; +import { $view } from "./view.schema"; + +export async function viewRoutes(fastify: FastifyInstance) { + fastify.post( + "/", + { + schema: { + body: $view("createViewInput"), + }, + config: { requiredClaims: ["view:write"] }, + preHandler: [fastify.authorize], + }, + createViewHandler + ); + + fastify.get( + "/", + { + schema: { + querystring: $view("pageQueryParams"), + }, + config: { requiredClaims: ["view:read"] }, + preHandler: [fastify.authorize], + }, + listViewHandler + ); + + fastify.get( + "/:viewId", + { + schema: { + params: { + type: "object", + properties: { + viewId: { type: "string" }, + }, + }, + }, + config: { requiredClaims: ["view:read"] }, + preHandler: [fastify.authorize], + }, + getViewHandler + ); + + fastify.patch( + "/:viewId", + { + schema: { + params: { + type: "object", + properties: { + viewId: { type: "string" }, + }, + }, + body: $view("updateViewInput"), + }, + config: { requiredClaims: ["view:write"] }, + preHandler: [fastify.authorize], + }, + updateViewHandler + ); + + fastify.delete( + "/:viewId", + { + schema: { + params: { + type: "object", + properties: { + viewId: { type: "string" }, + }, + }, + }, + config: { requiredClaims: ["view:delete"] }, + preHandler: [fastify.authorize], + }, + deleteViewHandler + ); +} diff --git a/src/view/view.service.ts b/src/view/view.service.ts index ecf096a..b047d8b 100644 --- a/src/view/view.service.ts +++ b/src/view/view.service.ts @@ -1,6 +1,12 @@ import { AuthenticatedUser } from "../auth"; +import { getFilterObject, getSortObject, PageQueryParams } from "../pagination"; import { generateId } from "../utils/id"; -import { CreateViewInput, UpdateViewInput, viewModel } from "./view.schema"; +import { + CreateViewInput, + UpdateViewInput, + viewFields, + viewModel, +} from "./view.schema"; export async function createView( input: CreateViewInput, @@ -20,7 +26,54 @@ export async function getView(viewId: string, tenantId: string) { }); } -export async function listViews() {} +export async function listViews(params: PageQueryParams, tenantId: string) { + const page = params.page || 1; + const pageSize = params.pageSize || 10; + const sortObj = getSortObject(params, viewFields); + const filterObj = getFilterObject(params, viewFields); + + const viewList = await viewModel.aggregate([ + { + $match: { $and: [{ tenantId: tenantId }, ...filterObj] }, + }, + { + $project: { + _id: 1, + pid: 1, + name: 1, + collection: 1, + createdBy: 1, + hide: 1, + sort: 1, + group: 1, + filters: 1, + match: 1, + }, + }, + { + $facet: { + metadata: [{ $count: "count" }], + data: [ + { $skip: (page - 1) * pageSize }, + { $limit: pageSize }, + { $sort: sortObj }, + ], + }, + }, + ]); + + if (viewList[0].data.length === 0) + return { views: [], metadata: { count: 0, page, pageSize } }; + + return { + permits: viewList[0]?.data, + metadata: { + count: viewList[0].metadata[0].count, + page, + pageSize, + }, + }; +} export async function updateView( viewId: string, @@ -29,10 +82,11 @@ export async function updateView( ) { return await viewModel.findOneAndUpdate( { $and: [{ pid: viewId }, { tenantId: tenantId }] }, - { ...input } + { ...input }, + { new: true } ); } -export async function deletView(viewId: string) { - return await viewModel.findOneAndDelete({ pid: viewId }); +export async function deleteView(viewId: string) { + return await viewModel.deleteOne({ pid: viewId }); }