-
Notifications
You must be signed in to change notification settings - Fork 0
Creating Inventory APIs #8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
b82c503
4174824
546df81
bf5de36
ce2d496
f471bb9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| { | ||
| "tabWidth": 4, | ||
| "useTabs": false, | ||
| "semi": true, | ||
| "singleQuote": false, | ||
| "quoteProps": "as-needed", | ||
| "trailingComma": "es5", | ||
| "bracketSpacing": true, | ||
| "arrowParens": "avoid" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| { | ||
| "editor.tabSize": 4, | ||
| "editor.insertSpaces": true, | ||
| "editor.detectIndentation": false, | ||
| "[typescript]": { | ||
| "editor.tabSize": 4 | ||
| }, | ||
| "[typescriptreact]": { | ||
| "editor.tabSize": 4 | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,136 @@ | ||
| import { NextResponse } from "next/server"; | ||
| import { z } from "zod"; | ||
| import { getItem, updateItem, deleteItem } from "@/services/items"; | ||
| import { | ||
| categoryValues, | ||
| notificationAudienceValues, | ||
| notificationEventValues, | ||
| } from "@/models/Item"; | ||
|
|
||
| const objectIdSchema = z | ||
| .string() | ||
| .regex(/^[0-9a-fA-F]{24}$/, "Invalid MongoDB ObjectId"); | ||
|
|
||
| const zEnumFromConst = <T extends readonly [string, ...string[]]>(values: T) => | ||
| z.enum(values as unknown as [T[number], ...T[number][]]); | ||
|
|
||
| export async function GET(_: Request, { params }: { params: { id: string } }) { | ||
| const parsedId = objectIdSchema.safeParse(params.id); | ||
| if (!parsedId.success) { | ||
| return NextResponse.json( | ||
| { message: parsedId.error.issues[0]?.message ?? "Invalid id" }, | ||
| { status: 400 } | ||
| ); | ||
| } | ||
|
|
||
| try { | ||
| const item = await getItem(parsedId.data); | ||
| if (!item) { | ||
| return NextResponse.json( | ||
| { message: "Item not found" }, | ||
| { status: 404 } | ||
| ); | ||
| } | ||
| return NextResponse.json(item, { status: 200 }); | ||
| } catch { | ||
| return NextResponse.json( | ||
| { success: false, message: "Error occured while retrieving items" }, | ||
| { status: 500 } | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| // PUT: update an item by id | ||
| export async function PUT( | ||
| request: Request, | ||
| { params }: { params: { id: string } } | ||
| ) { | ||
| const parsedId = objectIdSchema.safeParse(params.id); | ||
| if (!parsedId.success) { | ||
| return NextResponse.json({ message: "Invalid id" }, { status: 400 }); | ||
| } | ||
|
|
||
| const thresholdFullSchema = z.object({ | ||
| minQuantity: z.number().min(0), | ||
| enabled: z.boolean(), | ||
| lastAlertSentAt: z.coerce.date(), | ||
| }); | ||
|
|
||
| const notificationPolicyFullSchema = z.object({ | ||
| event: zEnumFromConst(notificationEventValues), | ||
| audience: zEnumFromConst(notificationAudienceValues), | ||
| }); | ||
|
|
||
| const updateSchema = z | ||
| .object({ | ||
| name: z.string().trim().min(1).optional(), | ||
| category: zEnumFromConst(categoryValues).optional(), | ||
| quantity: z.number().min(0).optional(), | ||
|
|
||
| threshold: thresholdFullSchema.optional(), | ||
| notificationPolicy: notificationPolicyFullSchema.optional(), | ||
| }) | ||
| .strict() | ||
| .refine(obj => Object.keys(obj).length > 0, { | ||
| message: "Body must include at least one field to update", | ||
| }); | ||
|
|
||
| // Assuming updateSchema | ||
| const json = await request.json(); | ||
| const parsedUpdate = updateSchema.safeParse(json); | ||
| if (!parsedUpdate.success) { | ||
| return NextResponse.json( | ||
| { | ||
| message: "Update doesn't follow schema", | ||
| issues: parsedUpdate.error.flatten(), | ||
| }, | ||
| { status: 400 } | ||
| ); | ||
| } | ||
|
|
||
| try { | ||
| const updated = await updateItem(parsedId.data, parsedUpdate.data); | ||
| if (!updated) { | ||
| return NextResponse.json( | ||
| { message: "Item not found" }, | ||
| { status: 404 } | ||
| ); | ||
| } | ||
|
|
||
| return NextResponse.json(updated, { status: 200 }); | ||
| } catch { | ||
| return NextResponse.json( | ||
| { success: false, message: "Error while updating data" }, | ||
| { status: 500 } | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| // In the future check for auth to prevent unauthorized deletes | ||
| // DELETE: Delete a product by id | ||
| export async function DELETE( | ||
| _: Request, | ||
| { params }: { params: { id: string } } | ||
| ) { | ||
| const parsedId = objectIdSchema.safeParse(params.id); | ||
| if (!parsedId.success) { | ||
| return NextResponse.json({ message: "Invalid id" }, { status: 400 }); | ||
| } | ||
|
|
||
| try { | ||
| const deleted = await deleteItem(parsedId.data); | ||
| if (!deleted) { | ||
| return NextResponse.json( | ||
| { message: "Item not found" }, | ||
| { status: 404 } | ||
| ); | ||
| } | ||
|
|
||
| return NextResponse.json(deleted, { status: 200 }); | ||
| } catch { | ||
| return NextResponse.json( | ||
| { success: false, message: "Error while deleting data" }, | ||
| { status: 500 } | ||
| ); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| import { NextResponse } from "next/server"; | ||
| import { z } from "zod"; | ||
| import { | ||
| categoryValues, | ||
| notificationAudienceValues, | ||
| notificationEventValues, | ||
| } from "@/models/Item"; | ||
| import { addItem, getItems } from "@/services/items"; | ||
| import { connectToDatabase } from "@/lib/mongoose"; | ||
|
|
||
| // Only returns a Next response upon failed connection | ||
| async function connect() { | ||
| try { | ||
| await connectToDatabase(); | ||
| } catch { | ||
| return NextResponse.json( | ||
| { success: false, message: "Error connecting to database" }, | ||
| { status: 500 } | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| const thresholdSchema = z.object({ | ||
| minQuantity: z.number().int().nonnegative(), | ||
| enabled: z.boolean(), | ||
| lastAlertSentAt: z.coerce.date(), | ||
| }); | ||
|
|
||
| const notificationPolicySchema = z.object({ | ||
| event: z.enum(notificationEventValues), | ||
| audience: z.enum(notificationAudienceValues), | ||
| }); | ||
|
|
||
| const itemCreateSchema = z.object({ | ||
| labId: z.string().min(1), | ||
| name: z.string().min(1), | ||
| category: z.enum(categoryValues), | ||
| quantity: z.number().int().nonnegative(), | ||
| threshold: thresholdSchema, | ||
| notificationPolicy: notificationPolicySchema, | ||
| }); | ||
|
|
||
| // GET: fetch all items | ||
arnavjk007 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| export async function GET() { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same thing, lets do the try catch with connectToDB before thsi try catch in all functs
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should also be filtered like they should be able to get by item name as well |
||
| const connectionResponse = await connect(); | ||
| // if (connectionResponse) return connectionResponse; ? | ||
|
|
||
| try { | ||
| const items = await getItems(); | ||
| return NextResponse.json(items, { status: 200 }); | ||
| } catch { | ||
| return NextResponse.json( | ||
| { message: "Failed to fetch items" }, | ||
| { status: 500 } | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| // POST: add a new item | ||
| export async function POST(request: Request) { | ||
arnavjk007 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| const connectionResponse = await connect(); | ||
|
|
||
| const body = await request.json(); | ||
| const parsedBody = itemCreateSchema.safeParse(body); | ||
|
|
||
| if (!parsedBody.success) { | ||
| return NextResponse.json( | ||
| { | ||
| success: false, | ||
| message: "Invalid request body.", | ||
| }, | ||
| { status: 400 } | ||
| ); | ||
| } | ||
|
|
||
| try { | ||
| const created = await addItem(parsedBody.data); | ||
| return NextResponse.json(created, { status: 201 }); | ||
| } catch { | ||
| return NextResponse.json( | ||
| { message: "Error occured while creating item" }, | ||
| { status: 500 } | ||
| ); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should connectToDB here instead of in services for consistency across all our services. We used a cached conn from:
import { connectToDatabase } from "@/lib/mongoose";
Put this before calling the services functions
try {
await connectToDatabase();
} catch {
return NextResponse.json(
{ success: false, message: "Error connecting to database." },
{ status: 500 }
);
}