WorkshopTasker/server/api/orders.post.ts

192 lines
6.6 KiB
TypeScript
Raw Normal View History

import { defineEventHandler, readBody, setResponseStatus } from "h3";
import * as Prisma from "@prisma/client";
2023-05-24 09:40:45 +02:00
import { createValidationError, handleRecursedValidationError } from "../utils/validation";
import { database } from "../utils/database";
2023-05-24 09:40:45 +02:00
import getRequestingUser from "../utils/getRequestingUser";
import { getOrder } from "./orders/[id].get";
import { prismaToWeb } from "~/server/utils/prismaToWeb";
2023-05-24 09:40:45 +02:00
import Snowflake from "~/utils/snowflake";
import { createError } from "#imports";
type importedProduct<inOrder extends boolean = boolean> = {
orderId: inOrder extends true ? never : string,
2023-05-24 09:40:45 +02:00
name: string | null,
link: string,
price_imported: number,
price: number,
}
type work<inOrder extends boolean = boolean> = {
orderId: inOrder extends true ? never : string,
offerId: string,
2023-05-24 09:40:45 +02:00
price: number,
notes: string | null,
fulfilled: boolean,
2023-05-24 09:40:45 +02:00
}
type order = {
clientId: string,
// userId: string,
draft: boolean,
imported_products: Array<importedProduct<true>>,
work: Array<work<true>>,
2023-05-24 09:40:45 +02:00
};
export function checkIsWork<Patch extends boolean = boolean, inOrder extends boolean = boolean>(
2023-05-24 09:40:45 +02:00
value: any,
patch: Patch,
needsOrderId: inOrder,
): value is Patch extends true ? Partial<work<inOrder>> : work<inOrder> {
2023-05-24 09:40:45 +02:00
const errors = new Map<string, string>();
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");
2023-05-24 09:40:45 +02:00
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
2023-05-24 09:40:45 +02:00
if (errors.size !== 0) throw createValidationError(errors);
return true;
}
export function checkIsImportedProduct<Patch extends boolean = boolean, inOrder extends boolean = boolean>(
2023-05-24 09:40:45 +02:00
value: any,
patch: Patch,
needsOrderId: inOrder,
): value is Patch extends true ? Partial<importedProduct<inOrder>> : importedProduct<inOrder> {
2023-05-24 09:40:45 +02:00
const errors = new Map<string, string>();
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");
2023-05-24 09:40:45 +02:00
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
2023-05-24 09:40:45 +02:00
if (errors.size !== 0) throw createValidationError(errors);
return true;
}
export function checkIsOrder<Patch extends boolean = boolean>(
value: any,
patch: Patch,
): value is Patch extends true ? Partial<Omit<order, "imported_products" | "work">> : order {
2023-05-24 09:40:45 +02:00
const errors = new Map<string, string>();
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");
2023-05-24 09:40:45 +02:00
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
2023-05-24 09:40:45 +02:00
if (!patch) {
const importedProducts = value.imported_products;
if (importedProducts instanceof Array) {
for (const i in importedProducts) {
try {
checkIsImportedProduct(importedProducts[i], patch, false);
2023-05-24 09:40:45 +02:00
} 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);
2023-05-24 09:40:45 +02:00
} 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;
2023-05-24 09:40:45 +02:00
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<Omit<Prisma.Prisma.ImportedProductCreateManyOrderInput, "orderId">>,
),
},
},
work: {
createMany: {
data: body.work.reduce(
(pV, cV) => {
pV.push({
...cV,
id: new Snowflake().state,
offerId: BigInt(cV.offerId),
});
return pV;
},
[] as Array<Omit<Prisma.Prisma.WorkCreateManyOrderInput, "orderId">>,
),
},
},
id,
userId: user.id,
},
});
2023-05-24 09:40:45 +02:00
setResponseStatus(e, 201);
return getOrder(id).then(prismaToWeb);
2023-05-24 09:40:45 +02:00
});