Add permits module

This commit is contained in:
2024-12-21 16:26:01 +05:30
parent cc2665544b
commit 6c1399ddd4
9 changed files with 412 additions and 2 deletions

View File

@@ -0,0 +1,87 @@
import { FastifyReply, FastifyRequest } from "fastify";
import { CreatePermitInput, UpdatePermitInput } from "./permit.schema";
import {
createPermit,
deletePermit,
getPermit,
listPermits,
updatePermit,
} from "./permit.service";
import { PageQueryParams } from "../pagination";
export async function createPermitHandler(
req: FastifyRequest,
res: FastifyReply
) {
const input = req.body as CreatePermitInput;
try {
const permit = await createPermit(input, req.user.tenantId);
return res.code(201).send(permit);
} catch (err) {
return err;
}
}
export async function getPermitHandler(req: FastifyRequest, res: FastifyReply) {
const { permitId } = req.params as { permitId: string };
try {
const permit = await getPermit(permitId, req.user.tenantId);
if (permit === null)
return res.code(404).send({ error: "resource not foound" });
return res.code(200).send(permit);
} catch (err) {
return err;
}
}
export async function listPermitsHandler(
req: FastifyRequest,
res: FastifyReply
) {
const queryParams = req.query as PageQueryParams;
try {
const authUser = req.user;
const orgList = await listPermits(queryParams, authUser.tenantId);
return res.code(200).send(orgList);
} catch (err) {
return err;
}
}
export async function updatePermitHandler(
req: FastifyRequest,
res: FastifyReply
) {
const input = req.body as UpdatePermitInput;
const { permitId } = req.params as { permitId: string };
try {
const updatedOrg = await updatePermit(input, permitId, req.user.tenantId);
if (!updatedOrg) return res.code(404).send({ error: "resource not found" });
return res.code(200).send(updatedOrg);
} catch (err) {
return err;
}
}
export async function deletePermitHandler(
req: FastifyRequest,
res: FastifyReply
) {
const { permitId } = req.params as { permitId: string };
try {
const deleteResult = await deletePermit(permitId, 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;
}
}

View File

@@ -0,0 +1,94 @@
import { FastifyInstance } from "fastify";
import {
createPermitHandler,
deletePermitHandler,
getPermitHandler,
listPermitsHandler,
updatePermitHandler,
} from "./permit.controller";
import { $permit } from "./permit.schema";
export async function permitRoutes(fastify: FastifyInstance) {
fastify.post(
"/",
{
schema: {
body: $permit("createPermitInput"),
response: {
201: $permit("createPermitResponse"),
},
},
config: { requiredClaims: ["permit:write"] },
preHandler: [fastify.authorize],
},
createPermitHandler
);
fastify.get(
"/:permitId",
{
schema: {
params: {
type: "object",
properties: {
permitId: { type: "string" },
},
},
response: {
200: $permit("getPermitResponse"),
},
},
config: { requiredClaims: ["permit:read"] },
preHandler: [fastify.authorize],
},
getPermitHandler
);
fastify.get(
"/",
{
schema: {
querystring: $permit("pageQueryParams"),
response: {
200: $permit("listPermitResponse"),
},
},
config: { requiredClaims: ["permit:read"] },
preHandler: [fastify.authorize],
},
listPermitsHandler
);
fastify.patch(
"/:permitId",
{
schema: {
params: {
type: "object",
properties: { permitId: { type: "string" } },
},
body: $permit("updatePermitInput"),
response: {
200: $permit("getPermitResponse"),
},
},
},
updatePermitHandler
);
fastify.delete(
"/:permitId",
{
schema: {
params: {
type: "object",
properties: { permitId: { type: "string" } },
},
},
config: { requiredClaims: ["permit:delete"] },
preHandler: [fastify.authorize],
},
deletePermitHandler
);
}

102
src/permit/permit.schema.ts Normal file
View File

@@ -0,0 +1,102 @@
import { z } from "zod";
import mongoose from "mongoose";
import { buildJsonSchemas } from "fastify-zod";
import { pageMetadata, pageQueryParams } from "../pagination";
export const permitModel = mongoose.model(
"permit",
new mongoose.Schema({
tenantId: {
type: String,
required: true,
},
pid: {
type: String,
unique: true,
},
permitNumber: String,
county: {
type: mongoose.Types.ObjectId,
ref: "organization",
},
client: {
type: mongoose.Types.ObjectId,
ref: "organization",
},
permitDate: Date,
stage: String,
status: String,
assignedTo: {
type: mongoose.Types.ObjectId,
ref: "user",
},
createdAt: Date,
updatedAt: Date,
createdBy: String,
}).index({ tenantId: 1, permitNumber: 1 }, { unique: true })
);
const permitCore = {
permitNumber: z.string(),
county: z.string().optional(),
client: z.string().optional(),
permitDate: z.date(),
stage: z.string().optional(),
status: z.string().optional(),
assignedTo: z.string().optional(),
};
const createPermitInput = z.object({
...permitCore,
});
const createPermitResponse = z.object({
pid: z.string(),
...permitCore,
});
const getPermitResponse = z.object({
pid: z.string(),
...permitCore,
county: z.object({
name: z.string(),
avatar: z.string().optional(),
}),
client: z.object({
name: z.string(),
avatar: z.string().optional(),
}),
assignedTo: z.object({
name: z.string(),
avatar: z.string().optional(),
}),
});
const listPermitResponse = z.object({
permits: z.array(getPermitResponse),
metadata: pageMetadata,
});
const updatePermitInput = z.object({
county: z.string().optional(),
client: z.string().optional(),
permitDate: z.date().optional(),
stage: z.string().optional(),
status: z.string().optional(),
assignedTo: z.string().optional(),
});
export type CreatePermitInput = z.infer<typeof createPermitInput>;
export type UpdatePermitInput = z.infer<typeof updatePermitInput>;
export const { schemas: permitSchemas, $ref: $permit } = buildJsonSchemas(
{
createPermitInput,
createPermitResponse,
getPermitResponse,
listPermitResponse,
updatePermitInput,
pageQueryParams,
},
{ $id: "permit" }
);

View File

@@ -0,0 +1,116 @@
import { PageQueryParams } from "../pagination";
import { generateId } from "../utils/id";
import {
CreatePermitInput,
permitModel,
UpdatePermitInput,
} from "./permit.schema";
export async function createPermit(input: CreatePermitInput, tenantId: string) {
const permit = await permitModel.create({
tenantId: tenantId,
pid: generateId(),
...input,
});
return permit;
}
export async function getPermit(permitId: string, tenantId: string) {
return await permitModel
.findOne({
$and: [{ tenantId: tenantId }, { pid: permitId }],
})
.populate("county")
.populate("client")
.populate("assignedTo");
}
export async function listPermits(params: PageQueryParams, tenantId: string) {
const page = params.page || 1;
const pageSize = params.pageSize || 10;
const permitsList = await permitModel.aggregate([
{
$match: { $and: [{ tenantId: tenantId }] },
},
{
$lookup: {
from: "organizations",
localField: "county",
foreignField: "_id",
as: "countyRec",
},
},
{
$lookup: {
from: "organizations",
localField: "client",
foreignField: "_id",
as: "clientRec",
},
},
{
$lookup: {
from: "permits",
localField: "assignedTo",
foreignField: "_id",
as: "assignedRec",
},
},
{
$project: {
_id: 0,
pid: 1,
permitNumber: 1,
county: { $arrayElemAt: ["$countyRec", 0] },
client: { $arrayElemAt: ["$clientRec", 0] },
permitDate: 1,
stage: 1,
status: 1,
assignedTo: { $arrayElemAt: ["$assignedRec", 0] },
},
},
{
$facet: {
metadata: [{ $count: "count" }],
data: [{ $skip: (page - 1) * pageSize }, { $limit: pageSize }],
},
},
]);
return {
permits: permitsList[0].data,
metadata: {
count: permitsList[0].metadata[0].count,
page,
pageSize,
},
};
}
export async function updatePermit(
input: UpdatePermitInput,
permitId: string,
tenantId: string
) {
const updatePermitResult = await permitModel
.findOneAndUpdate(
{
$and: [{ tenantId: tenantId }, { pid: permitId }],
},
{ ...input, updatedAt: new Date() },
{ new: true }
)
.populate("county")
.populate("client")
.populate("assignedTo");
return updatePermitResult;
}
export async function deletePermit(permitId: string, tenantId: string) {
return await permitModel.deleteOne({
$and: [{ tenantId: tenantId }, { pid: permitId }],
});
}