-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
implements section heading controller, router, and tests
- Loading branch information
1 parent
b73a348
commit d08e989
Showing
4 changed files
with
343 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 }, | ||
); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" }); | ||
} | ||
} | ||
}, | ||
); | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"); | ||
}); | ||
}); |