From 4c0be6d87bd6a0ce2fc1bdc2346b0c9caf0f7e60 Mon Sep 17 00:00:00 2001 From: Spacelord Date: Tue, 27 Jun 2023 23:45:58 +0200 Subject: [PATCH] Implemented api validation and item route patch - Implemented api field validation in item api routes - Implemented PATCH api route for item endpoint --- src/assets/helper.ts | 12 +++++ src/routes/api/v1/items.ts | 97 +++++++++++++++++++++++--------------- 2 files changed, 71 insertions(+), 38 deletions(-) diff --git a/src/assets/helper.ts b/src/assets/helper.ts index b56fb28..5b6701d 100644 --- a/src/assets/helper.ts +++ b/src/assets/helper.ts @@ -70,9 +70,21 @@ export function parseIntRelation(data: string, relation_name: string = 'id') { // This function is perfect. If data is not a valid number, return `undefined` // If it is a valid number return `{connect: {relation_name: yourNumber}}}` // This can be used by prisma to connect relations + + // If the incoming data is null or empty, return a prisma disconnect object instead of a connect one + if (data === null || data === '') { + return JSON.parse(`{ + "disconnect": true + }`); + } + return isNaN(parseInt(data)) ? undefined : JSON.parse(`{ "connect": { "${relation_name}": ${parseInt(data)} } }`); } + +export function parseIntOrUndefined(data: string) { + return isNaN(parseInt(data)) ? undefined : parseInt(data); +} diff --git a/src/routes/api/v1/items.ts b/src/routes/api/v1/items.ts index 27a1ab7..2e5ed29 100644 --- a/src/routes/api/v1/items.ts +++ b/src/routes/api/v1/items.ts @@ -1,21 +1,27 @@ import { Request, Response } from 'express'; import { prisma, __path, log } from '../../../index.js'; -//import { itemStatus } from '@prisma/client'; - +import { itemStatus } from '@prisma/client'; +import { parseIntRelation, parseIntOrUndefined } from '../../../assets/helper.js'; // Get item. function get(req: Request, res: Response) { if (req.query.getAll === undefined) { - // Check if required fields are present. + // Check if required fields are present if (!req.query.id) { res.status(400).json({ errorcode: 'VALIDATION_ERROR', error: 'One or more required fields are missing' }); return; } + + // Check if number is a valid integer + if (!Number.isInteger(parseInt(req.query.id.toString()))) { + res.status(400).json({ errorcode: 'VALIDATION_ERROR', error: 'The id field must be an integer' }); + return; + } prisma.item .findUnique({ where: { id: parseInt(req.query.id.toString()) }, - // Get contactInfo, category, storageLocation( storageUnit ) from relations. + // Get contactInfo, category, storageLocation( storageUnit ) from relations include: { contactInfo: true, category: true, @@ -38,7 +44,7 @@ function get(req: Request, res: Response) { } }) .catch((err) => { - console.error(err); + log.db.error(err); res.status(500).json({ errorcode: 'DB_ERROR', error: err }); }); } else { @@ -67,7 +73,7 @@ function get(req: Request, res: Response) { } }) .catch((err) => { - console.error(err); + log.db.error(err); res.status(500).json({ errorcode: 'DB_ERROR', error: err }); }); } @@ -81,19 +87,24 @@ function post(req: Request, res: Response) { return; } + // Check if status is valid. + if (req.body.status !== undefined && !Object.keys(itemStatus).includes(req.body.status)) { + res.status(400).json({ errorcode: 'VALIDATION_ERROR', error: `Status is not valid, valid values are: ${Object.keys(itemStatus).join(', ')}` }); + return; + } + prisma.item .create({ data: { SKU: req.body.sku, - amount: req.body.ammount, + amount: parseIntOrUndefined(req.body.ammount), // FIXME: This is silently failing if NaN.. name: req.body.name, comment: req.body.comment, - status: req.body.status, //itemStatus.normal, - + status: req.body.status, // Only enum(itemStatus) values are valid // Relations - contactInfoId: req.body.contactInfoId, - categoryId: req.body.categoryId, - storageLocationId: req.body.storageLocationId, + contactInfo: parseIntRelation(req.body.contactInfoId), + category: parseIntRelation(req.body.categoryId), + storageLocation: parseIntRelation(req.body.storageLocationId), manufacturer: req.body.manufacturer, @@ -109,7 +120,6 @@ function post(req: Request, res: Response) { } }) .then((data) => { - // TODO: Check if id is returned correctly res.status(201).json({ status: 'created', id: data.id }); }) .catch((err) => { @@ -117,12 +127,12 @@ function post(req: Request, res: Response) { if (err.code === 'P2002') { // P2002 -> "Unique constraint failed on the {constraint}" // https://www.prisma.io/docs/reference/api-reference/error-reference - res.status(409).json({ errorcode: 'EXISTING', error: 'storageLocation already exists' }); + res.status(409).json({ errorcode: 'EXISTING', error: 'Item already exists' }); } else if (err.code == 'P2003') { // P2003 -> "Foreign key constraint failed on the field: {field_name}" // https://www.prisma.io/docs/reference/api-reference/error-reference // FIXME: Is this errormessage right? - res.status(404).json({ errorcode: 'NOT_EXISTING', error: 'specified storageUnitId does not exist' }); + res.status(404).json({ errorcode: 'NOT_EXISTING', error: 'Specified item does not exist' }); } else { log.db.error(err); res.status(500).json({ errorcode: 'DB_ERROR', error: err }); @@ -132,39 +142,51 @@ function post(req: Request, res: Response) { // Update storageLocation. -> Only existing contactInfo. async function patch(req: Request, res: Response) { - res.status(501).json({ errorcode: 'NOT_IMPLEMENTED', error: 'This endpoint is not yet implemented' }); - /* // Check if required fields are present. - if (!req.body.id || !req.body.name || !req.body.storageUnitId) { + if (!req.body.id) { res.status(400).json({ errorcode: 'VALIDATION_ERROR', error: 'One or more required fields are missing' }); return; } - // Check if the storageLocation id exists. If not return 410 Gone. - try { - const result = await prisma.storageLocation.findUnique({ - where: { - id: parseInt(req.body.id) - } - }); - - if (result === null) { - res.status(404).json({ errorcode: 'NOT_EXISTING', error: 'specified storageUnitId does not exist' }); - return; - } - } catch (err) { - log.db.error(err); - res.status(500).json({ errorcode: 'DB_ERROR', error: err }); + // Check if number is a valid integer + if (!Number.isInteger(parseInt(req.body.id.toString()))) { + res.status(400).json({ errorcode: 'VALIDATION_ERROR', error: 'The id field must be an integer' }); + return; } - prisma.storageLocation + // Check if status is valid. + if (req.body.status !== undefined && !Object.keys(itemStatus).includes(req.body.status)) { + res.status(400).json({ errorcode: 'VALIDATION_ERROR', error: `Status is not valid, valid values are: ${Object.keys(itemStatus).join(', ')}` }); + return; + } + + prisma.item .update({ where: { id: parseInt(req.body.id) }, data: { + SKU: req.body.sku, + amount: parseIntOrUndefined(req.body.ammount), // FIXME: This is silently failing if NaN.. name: req.body.name, - storageUnitId: parseInt(req.body.storageUnitId) || undefined + comment: req.body.comment, + status: req.body.status, // Only enum(itemStatus) values are valid + // Relations + contactInfo: parseIntRelation(req.body.contactInfoId), + category: parseIntRelation(req.body.categoryId), + storageLocation: parseIntRelation(req.body.storageLocationId), + + manufacturer: req.body.manufacturer, + + //contents: { + // connect: [{ id: 1 }, { id: 2 }, { id: 3 }] + //}, + //baseItem: { + // connect: { + // id: req.body.baseitemId + // } + //}, + createdBy: req.body.createdBy } }) .then(() => { @@ -175,17 +197,16 @@ async function patch(req: Request, res: Response) { if (err.code === 'P2002') { // P2002 -> "Unique constraint failed on the {constraint}" // https://www.prisma.io/docs/reference/api-reference/error-reference - res.status(409).json({ errorcode: 'EXISTING', error: 'storageLocation already exists' }); + res.status(409).json({ errorcode: 'EXISTING', error: 'Item already exists', err: err }); } else if (err.code == 'P2003') { // P2003 -> "Foreign key constraint failed on the field: {field_name}" // https://www.prisma.io/docs/reference/api-reference/error-reference - res.status(404).json({ error: 'specified storageUnitId does not exist' }); + res.status(404).json({ errorcode: 'NOT_EXISTING', error: 'Specified item does not exist' }); } else { log.db.error(err); res.status(500).json({ errorcode: 'DB_ERROR', error: err }); } }); -*/ } // Delete item.