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

@@ -23,12 +23,13 @@ export const orgModel = mongoose.model(
isClient: Boolean,
status: String,
createdAt: Date,
createdBy: mongoose.Types.ObjectId,
createdBy: String,
updatedAt: Date,
}).index({ tenantId: 1, domain: 1 }, { unique: true })
);
const orgCore = {
_id: z.string().optional(),
name: z.string().max(30),
domain: z.string().max(30),
avatar: z.string().url().optional(),

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 }],
});
}

View File

@@ -2,9 +2,11 @@ import { FastifyInstance } from "fastify";
import userRoutes from "./user/user.route";
import organizationRoutes from "./organization/organization.route";
import { tokenRoutes } from "./tokens/token.route";
import { permitRoutes } from "./permit/permit.route";
export default async function routes(fastify: FastifyInstance) {
fastify.register(userRoutes, { prefix: "/users" });
fastify.register(organizationRoutes, { prefix: "/orgs" });
fastify.register(tokenRoutes, { prefix: "/tokens" });
fastify.register(permitRoutes, { prefix: "/permits" });
}

View File

@@ -7,6 +7,7 @@ import { orgSchemas } from "./organization/organization.schema";
import { tokenSchemas } from "./tokens/token.schema";
import { errorHandler } from "./utils/errors";
import { authHandler, authorize } from "./auth";
import { permitSchemas } from "./permit/permit.schema";
const app = fastify({ logger: true });
@@ -19,7 +20,12 @@ app.setErrorHandler(errorHandler);
app.addHook("onRequest", authHandler);
app.register(routes, { prefix: "/api/v1" });
for (const schema of [...userSchemas, ...orgSchemas, ...tokenSchemas]) {
for (const schema of [
...userSchemas,
...orgSchemas,
...tokenSchemas,
...permitSchemas,
]) {
app.addSchema(schema);
}

View File

@@ -16,6 +16,7 @@ export const userModel = mongoose.model(
},
firstName: String,
lastName: String,
name: String,
email: {
type: String,
unique: true,

View File

@@ -5,6 +5,7 @@ export async function createUser(input: CreateUserInput, tenantId: string) {
const user = await userModel.create({
tenantId: tenantId,
pid: generateId(),
name: input.firstName + " " + input.lastName,
createdAt: new Date(),
...input,
});