Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ node_modules/
.next/
.env.local
.DS_Store
.env
.env
coverage/
216 changes: 216 additions & 0 deletions app/api/listings/[id]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
import { NextResponse } from "next/server";
import { connectToDatabase } from "@/lib/mongoose";
import { z } from "zod";
import {
deleteListing,
getListing,
updateListing,
} from "@/services/listings/listings";

/* IMPORTANT: implement user auth in future (e.g. only lab admins create/delete) */
const objectIdSchema = z
.string()
.regex(/^[0-9a-fA-F]{24}$/, "Invalid MongoDB ObjectId");
const listingValidationSchema = z.object({
// handle defaults here for the optional fields
itemName: z.string(),
itemId: z.string(),
labName: z.string().optional().default(""),
labLocation: z.string().optional().default(""),
labId: z.string(),
imageUrls: z.array(z.string()).optional().default([]),
quantityAvailable: z.number(),
expiryDate: z.date().optional(),
description: z.string().optional().default(""),
price: z.number().optional().default(0),
status: z.enum(["ACTIVE", "INACTIVE"]),
condition: z.enum(["New", "Good", "Fair", "Poor"]),
hazardTags: z
.array(z.enum(["Physical", "Chemical", "Biological", "Other"]))
.optional()
.default([]),
});

/**
* Get a listing entry by ID
* @param id the ID of the listing to get
* ex req: GET /listings/001 HTTP/1.1
* @returns the listing as a JS object in a JSON response
*/
async function GET(request: Request, { params }: { params: { id: string } }) {
try {
await connectToDatabase();
} catch {
return NextResponse.json(
{ success: false, message: "Error connecting to database." },
{ status: 500 }
);
}

const parsedId = objectIdSchema.safeParse(params.id);
if (!parsedId.success) {
return NextResponse.json(
{
success: false,
message: "Invalid ID format. Must be a valid MongoDB ObjectId.",
},
{ status: 400 }
);
}

try {
const listing = await getListing(parsedId.data); // don't need mongo doc features
if (!listing) {
return NextResponse.json(
{ success: false, message: "Listing not found." },
{ status: 404 }
);
}
return NextResponse.json({ success: true, data: listing }, { status: 200 });
} catch {
return NextResponse.json(
{ success: false, message: "Error occurred while retrieving listing." },
{ status: 500 }
);
}
}

/**
* Update a listing entry by ID
* @param id the ID of the listing to get as part of the path params
* @returns the updated listing as a JS object in a JSON response
*/
async function PUT(request: Request, { params }: { params: { id: string } }) {
try {
await connectToDatabase();
} catch {
return NextResponse.json(
{ success: false, message: "Error connecting to database." },
{ status: 500 }
);
}

const parsedId = objectIdSchema.safeParse(params.id);
if (!parsedId.success) {
return NextResponse.json(
{
success: false,
message: "Invalid ID format. Must be a valid MongoDB ObjectId.",
},
{ status: 400 }
);
}

const body = await request.json();

const validator = z.object({
id: objectIdSchema,
update: listingValidationSchema.partial(),
});

const parsedRequest = validator.safeParse({
id: parsedId.data,
update: body,
});
if (!parsedRequest.success) {
return NextResponse.json(
{
success: false,
message: "Invalid request body.",
},
{ status: 400 }
);
}

try {
const updatedListing = await updateListing(
parsedId.data,
parsedRequest.data.update
);
if (!updatedListing) {
return NextResponse.json(
{
success: false,
message: "Listing not found",
},
{ status: 404 }
);
}
return NextResponse.json(
{
success: true,
data: updatedListing,
message: "Listing successfully updated.",
},
{ status: 200 }
);
} catch {
return NextResponse.json(
{
success: false,
message: "Error occurred while updating listing.",
},
{ status: 500 }
);
}
}

/**
* Delete a listing entry by ID
* @param id the ID of the listing to get as part of the path params
* @returns JSON response signaling the success of the listing deletion
*/
async function DELETE(
request: Request,
{ params }: { params: { id: string } }
) {
try {
await connectToDatabase();
} catch {
return NextResponse.json(
{ success: false, message: "Error connecting to database." },
{ status: 500 }
);
}

const parsedId = objectIdSchema.safeParse(params.id);
if (!parsedId.success) {
return NextResponse.json(
{
success: false,
message: "Invalid ID format. Must be a valid MongoDB ObjectId.",
},
{ status: 400 }
);
}

try {
const listing = await deleteListing(parsedId.data);
if (!listing) {
return NextResponse.json(
{
success: false,
message: "Listing not found",
},
{ status: 404 }
);
}
return NextResponse.json(
{
success: true,
message: "Listing successfully deleted.",
},
{ status: 204 }
);
} catch {
return NextResponse.json(
{
success: false,
message: "Error occurred while deleting listing.",
},
{ status: 500 }
);
}
}

export { GET, PUT, DELETE };
Loading