Skip to content

Commit

Permalink
implements section heading controller, router, and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
jackgarritano committed Mar 3, 2024
1 parent b73a348 commit d08e989
Show file tree
Hide file tree
Showing 4 changed files with 343 additions and 1 deletion.
148 changes: 148 additions & 0 deletions src/controllers/sectionHeading.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import { SectionHeadingModel, type SectionHeadingType } from "../models/sectionHeading.model";
import { ResumeModel } from "../models/resume.model";
import mongoose from "mongoose";
import { HttpError, HttpStatus, checkMongooseErrors } from "../utils/errors";
import { checkDuplicateItemName } from "../utils/checkDuplicates";

export const createSectionHeading = async (sectionHeadingsFields: SectionHeadingType) => {
try {
if(await checkDuplicateItemName(sectionHeadingsFields.itemName)){
throw new HttpError(
HttpStatus.BAD_REQUEST,
"Duplicate item name",
)
}

const newSectionHeadings = new SectionHeadingModel(sectionHeadingsFields);
await newSectionHeadings.save();
return newSectionHeadings;
} catch (err: unknown) {
if (err instanceof HttpError) {
throw err;
}

checkMongooseErrors(err);

throw new HttpError(
HttpStatus.INTERNAL_SERVER_ERROR,
"SectionHeading creation failed",
{ cause: err },
);
}
};

export const getAllSectionHeadings = async (user: string) => {
try {
const sectionHeadings = await SectionHeadingModel.find({ user: user });
return sectionHeadings;
} catch (err: unknown) {
//rethrow any errors as HttpErrors
if (err instanceof HttpError) {
throw err;
}
//checks if mongoose threw and will rethrow with appropriate status code and message
checkMongooseErrors(err);

throw new HttpError(
HttpStatus.INTERNAL_SERVER_ERROR,
"SectionHeadings retrieval failed",
{ cause: err },
);
}
}

export const getSectionHeadingById = async (user: string, sectionHeadingId: string) => {
try {
const sectionHeading = await SectionHeadingModel.findOne({
user: user,
_id: sectionHeadingId,
});
return sectionHeading;
} catch (err: unknown) {
//rethrow any errors as HttpErrors
if (err instanceof HttpError) {
throw err;
}
//checks if mongoose threw and will rethrow with appropriate status code and message
checkMongooseErrors(err);

throw new HttpError(
HttpStatus.INTERNAL_SERVER_ERROR,
"SectionHeading retrieval failed",
{ cause: err },
);
}
};

export const updateSectionHeading = async (
user: string,
sectionHeadingId: string,
sectionHeadingsFields: SectionHeadingType,
) => {
try {
if (!sectionHeadingId) {
throw new HttpError(
HttpStatus.BAD_REQUEST,
"Missing sectionHeading ID for update",
);
}

if (await checkDuplicateItemName(sectionHeadingsFields.itemName, sectionHeadingId)) {
throw new HttpError(HttpStatus.BAD_REQUEST, "Duplicate item name");
}

const updatedSectionHeading = await SectionHeadingModel.findOneAndUpdate(
{ _id: sectionHeadingId, user: user }, // Query to match the document by _id and user
{ $set: sectionHeadingsFields }, // Update operation
{ new: true, runValidators: true }, // Options: return the updated document and run schema validators
);
return updatedSectionHeading;
} catch (err: unknown) {
//rethrow any errors as HttpErrors
if (err instanceof HttpError) {
throw err;
}
//checks if mongoose threw and will rethrow with appropriate status code and message
checkMongooseErrors(err);

throw new HttpError(
HttpStatus.INTERNAL_SERVER_ERROR,
"SectionHeading update failed",
{ cause: err },
);
}
};

export const deleteSectionHeading = async (user: string, sectionHeadingId: string) => {
try {
await ResumeModel.updateMany(
{ itemIds: new mongoose.Types.ObjectId(sectionHeadingId) },
{ $pull: { itemIds: new mongoose.Types.ObjectId(sectionHeadingId) } }
);

const deletedSectionHeading = await SectionHeadingModel.findOneAndDelete({
_id: sectionHeadingId,
user: user,
});
if (!deletedSectionHeading) {
throw new HttpError(
HttpStatus.NOT_FOUND,
"SectionHeading not found or already deleted",
);
}
return { message: "SectionHeading deleted successfully" };
} catch (err: unknown) {
//rethrow any errors as HttpErrors
if (err instanceof HttpError) {
throw err;
}
//checks if mongoose threw and will rethrow with appropriate status code and message
checkMongooseErrors(err);

throw new HttpError(
HttpStatus.INTERNAL_SERVER_ERROR,
"SectionHeading deletion failed",
{ cause: err },
);
}
};
119 changes: 119 additions & 0 deletions src/routers/sectionHeading.router.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { Router, type Request, type Response } from "express";
import {
createSectionHeading,
getAllSectionHeadings,
getSectionHeadingById,
updateSectionHeading,
deleteSectionHeading,
} from "../controllers/sectionHeading.controller";
import { HttpError, HttpStatus } from "../utils/errors";
import { type SectionHeadingType } from "../models/sectionHeading.model";

export const sectionHeadingRouter = Router();

//Add an sectionHeading
//Note that the user field (which is part of SectionHeadingType) in body is automatically populated by verifyToken middleware
sectionHeadingRouter.post(
"/",
async (req: Request<any, any, SectionHeadingType>, res: Response) => {
try {
const sectionHeading = await createSectionHeading(req.body);
res.status(HttpStatus.OK).json(sectionHeading);
} catch (err: unknown) {
if (err instanceof HttpError) {
res.status(err.errorCode).json({ error: err.message });
} else {
res
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.json({ error: "An unknown error occurred" });
}
}
},
);

//Get all sectionHeading
sectionHeadingRouter.get(
"/",
async (req: Request<any, any, SectionHeadingType>, res: Response) => {
try {
const sectionHeading = await getAllSectionHeadings(req.body.user);
res.status(HttpStatus.OK).json(sectionHeading);
} catch (err: unknown) {
if (err instanceof HttpError) {
res.status(err.errorCode).json({ error: err.message });
} else {
res
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.json({ error: "An unknown error occurred" });
}
}
},
);

//Get a single sectionHeading by id
sectionHeadingRouter.get(
"/:sectionHeadingId",
async (req: Request<any, any, SectionHeadingType>, res: Response) => {
try {
const sectionHeading = await getSectionHeadingById(
req.body.user,
req.params.sectionHeadingId,
);
res.status(HttpStatus.OK).json(sectionHeading);
} catch (err: unknown) {
if (err instanceof HttpError) {
res.status(err.errorCode).json({ error: err.message });
} else {
res
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.json({ error: "An unknown error occurred" });
}
}
},
);

//Update an sectionHeading
sectionHeadingRouter.put(
"/:sectionHeadingId",
async (req: Request<any, any, SectionHeadingType>, res: Response) => {
try {
const sectionHeading = await updateSectionHeading(
req.body.user,
req.params.sectionHeadingId,
req.body,
);
res.status(HttpStatus.OK).json(sectionHeading);
} catch (err: unknown) {
if (err instanceof HttpError) {
res.status(err.errorCode).json({ error: err.message });
} else {
res
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.json({ error: "An unknown error occurred" });
}
}
},
);

//Delete an sectionHeading
sectionHeadingRouter.delete(
"/:sectionHeadingId",
async (req: Request<any, any, SectionHeadingType>, res: Response) => {
try {
const sectionHeading = await deleteSectionHeading(
req.body.user,
req.params.sectionHeadingId,
);
res.status(HttpStatus.OK).json(sectionHeading);
} catch (err: unknown) {
if (err instanceof HttpError) {
res.status(err.errorCode).json({ error: err.message });
} else {
res
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.json({ error: "An unknown error occurred" });
}
}
},
);

6 changes: 5 additions & 1 deletion src/tests/controllers.tests/dummyData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,8 @@ export const resumeDummyData1 = {
templateId: new mongoose.Types.ObjectId("75e4f54db1e12e776e01cf31"),
}


export const sectionHeadingDummyData1 = {
user: "test",
itemName: "sectionHeadingItem1",
title: "test section heading",
}
71 changes: 71 additions & 0 deletions src/tests/controllers.tests/sectionHeading.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { dbConnect, dbDisconnect } from "../dbHandler";
import { type SectionHeadingType } from "../../models/sectionHeading.model";
import { sectionHeadingDummyData1 } from "./dummyData";
import {
createSectionHeading,
getAllSectionHeadings,
getSectionHeadingById,
updateSectionHeading,
deleteSectionHeading,
} from "../../controllers/sectionHeading.controller";
import { describe, test, expect, beforeEach, afterEach } from "vitest";

describe("SectionHeadings controller tests", () => {
beforeEach(async () => dbConnect());
afterEach(async () => dbDisconnect());

test("Adds and retrieves an sectionHeadings", async () => {
await createSectionHeading(sectionHeadingDummyData1 as SectionHeadingType);
const returnedSectionHeadings = await getAllSectionHeadings(sectionHeadingDummyData1.user);

//get back the 1 sectionHeadings that was added
expect(returnedSectionHeadings.length).to.equal(1);
expect(returnedSectionHeadings[0]).toMatchObject(sectionHeadingDummyData1);

//Can't add duplicate name
await expect(
createSectionHeading(sectionHeadingDummyData1 as SectionHeadingType),
).rejects.toThrowError();

const returnedSectionHeadings2 = await getAllSectionHeadings(sectionHeadingDummyData1.user);

//if duplicate, shouldn't add to db
expect(returnedSectionHeadings2.length).to.equal(1);

const returnedSectionHeadings3 = await getAllSectionHeadings("fakeuserid");

//don't get records for a different user id
expect(returnedSectionHeadings3.length).to.equal(0);
});

test("Finds, updates, and deletes an sectionHeadings", async () => {
await createSectionHeading(sectionHeadingDummyData1 as SectionHeadingType);
const returnedEd = await getAllSectionHeadings(sectionHeadingDummyData1.user);

const returnedSectionHeadings = await getSectionHeadingById(
sectionHeadingDummyData1.user,
returnedEd[0]._id,
);

expect(returnedSectionHeadings).toMatchObject(sectionHeadingDummyData1);

const newItemName = "sectionHeadingsItem2";
await updateSectionHeading(sectionHeadingDummyData1.user, returnedEd[0]._id, {
...sectionHeadingDummyData1,
itemName: newItemName,
} as SectionHeadingType);
const returnedSectionHeadings2 = await getSectionHeadingById(
sectionHeadingDummyData1.user,
returnedEd[0]._id,
);
expect(returnedSectionHeadings2?.itemName).to.equal(newItemName);

await deleteSectionHeading(sectionHeadingDummyData1.user, returnedEd[0]._id);
const returnedSectionHeadings3 = await getAllSectionHeadings(sectionHeadingDummyData1.user);
expect(returnedSectionHeadings3.length).to.equal(0);

await expect(
updateSectionHeading(sectionHeadingDummyData1.user, "", {} as SectionHeadingType),
).rejects.toThrowError("Missing");
});
});

0 comments on commit d08e989

Please sign in to comment.