import { defineEventHandler, readBody, setResponseStatus } from "h3"; import * as Prisma from "@prisma/client"; import { createValidationError, handleRecursedValidationError } from "../utils/validation"; import { database } from "../utils/database"; import getRequestingUser from "../utils/getRequestingUser"; import { getOrder } from "./orders/[id].get"; import { prismaToWeb } from "~/server/utils/prismaToWeb"; import Snowflake from "~/utils/snowflake"; import { createError } from "#imports"; type importedProduct = { orderId: inOrder extends true ? never : string, name: string | null, link: string, price_imported: number, price: number, } type work = { orderId: inOrder extends true ? never : string, offerId: string, price: number, notes: string | null, fulfilled: boolean, } type order = { clientId: string, // userId: string, draft: boolean, imported_products: Array>, work: Array>, }; export function checkIsWork( value: any, patch: Patch, needsOrderId: inOrder, ): value is Patch extends true ? Partial> : work { const errors = new Map(); if (typeof value !== "object") { throw createError({ message: "Invalid body", statusCode: 400, }); } if (!(typeof value.orderId === "string" || (patch && value.orderId === undefined) || !needsOrderId)) errors.set("orderId", "is not string"); if (!(typeof value.offerId === "string" || (patch && value.offerId === undefined))) errors.set("offerId", "is not string"); if (!(typeof value.price === "number" || (patch && value.price === undefined))) errors.set("price", "is not price"); if (!(typeof value.notes === "string" || value.notes === null || (patch && value.notes === undefined))) errors.set("notes", "is not string or null"); if (!(typeof value.is_fulfilled === "boolean" || (patch && value.is_fulfilled === undefined))) errors.set("is_fulfilled", "is not boolean"); // TODO: Excessive property checking // Excessive properties should be checked and an error should be thrown if there is one if (errors.size !== 0) throw createValidationError(errors); return true; } export function checkIsImportedProduct( value: any, patch: Patch, needsOrderId: inOrder, ): value is Patch extends true ? Partial> : importedProduct { const errors = new Map(); if (typeof value !== "object") { throw createError({ message: "Invalid body", statusCode: 400, }); } if (!(typeof value.orderId === "string" || (patch && value.orderId === undefined) || !needsOrderId)) errors.set("orderId", "is not string"); if (!(typeof value.name === "string" || value.name === null || (patch && value.name === undefined))) errors.set("name", "is not string or null"); if (!(typeof value.link === "string" || (patch && value.name === undefined))) errors.set("link", "is not string"); if (!(typeof value.price_imported === "number" || (patch && value.name === undefined))) errors.set("price_imported", "is not number"); if (!(typeof value.price === "number" || (patch && value.price === undefined))) errors.set("price", "is not number"); // TODO: Excessive property checking // Excessive properties should be checked and an error should be thrown if there is one if (errors.size !== 0) throw createValidationError(errors); return true; } export function checkIsOrder( value: any, patch: Patch, ): value is Patch extends true ? Partial> : order { const errors = new Map(); if (typeof value !== "object") { throw createError({ message: "Invalid body", statusCode: 400, }); } if (!(typeof value.clientId === "string" || (patch && value.clientId === undefined))) errors.set("clientId", "is not string"); if (!(typeof value.draft === "boolean" || (patch && value.is_draft === undefined))) errors.set("draft", "is not boolean"); if (!(value.imported_products instanceof Array)) errors.set("imported_products", "is not array"); else if (patch && value.imported_products !== undefined) errors.set("imported_products", "cannot patch from order"); if (!(value.work instanceof Array)) errors.set("work", "is not array"); else if (patch && value.work !== undefined) errors.set("work", "cannot patch from order"); // TODO: Excessive property checking // Excessive properties should be checked and an error should be thrown if there is one if (!patch) { const importedProducts = value.imported_products; if (importedProducts instanceof Array) { for (const i in importedProducts) { try { checkIsImportedProduct(importedProducts[i], patch, false); } catch (e) { handleRecursedValidationError(e, errors, `imported_products[${i}]`); } } } const work = value.work; if (work instanceof Array) { for (const i in work) { try { checkIsWork(work[i], patch, false); } catch (e) { handleRecursedValidationError(e, errors, `work[${i}]`); } } } } if (errors.size !== 0) throw createValidationError(errors); return true; } export default defineEventHandler(async (e) => { const body = await readBody(e); const id = new Snowflake().state; const user = await getRequestingUser(e); if (!checkIsOrder(body, false)) throw createError({ message: "Invalid body", statusCode: 400 }); await database.order.create({ data: { clientId: BigInt(body.clientId), draft: body.draft, imported_products: { createMany: { data: body.imported_products.reduce( (pV, cV) => { pV.push({ ...cV, id: new Snowflake().state, }); return pV; }, [] as Array>, ), }, }, work: { createMany: { data: body.work.reduce( (pV, cV) => { pV.push({ ...cV, id: new Snowflake().state, offerId: BigInt(cV.offerId), }); return pV; }, [] as Array>, ), }, }, id, userId: user.id, }, }); setResponseStatus(e, 201); return getOrder(id).then(prismaToWeb); });