Migrated validation to joi / Moved prisma_helpers to dedicated file /
This commit is contained in:
parent
be1544a46f
commit
c96bdfddb0
22
src/helpers/prisma_helpers.ts
Normal file
22
src/helpers/prisma_helpers.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* A function to create a sortBy compatible object from a string
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {string} SortField
|
||||||
|
* @param {string} Order
|
||||||
|
* @returns {object}
|
||||||
|
*/
|
||||||
|
export function parseDynamicSortBy(SortField: string, Order: string) {
|
||||||
|
return JSON.parse(`{ "${SortField}": "${Order}" }`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to parse a string into a number or return undefined if it is not a number
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {string || any} data
|
||||||
|
* @returns {object}
|
||||||
|
*/
|
||||||
|
export function parseIntOrUndefined(data: any) {
|
||||||
|
return isNaN(parseInt(data)) ? undefined : parseInt(data);
|
||||||
|
}
|
@ -1,116 +1,87 @@
|
|||||||
import { Request, Response } from 'express';
|
import { Request, Response } from 'express';
|
||||||
import db, { handlePrismaError } from '../../../handlers/db.js'; // Database
|
import db, { handlePrismaError } from '../../../handlers/db.js'; // Database
|
||||||
import log from '../../../handlers/log.js';
|
import log from '../../../handlers/log.js';
|
||||||
|
import { parseIntOrUndefined, parseDynamicSortBy } from '../../../helpers/prisma_helpers.js';
|
||||||
|
import { schema_get } from './alertContacts_schema.js';
|
||||||
|
|
||||||
///api/v1/alertContacts?action=count&filter=...
|
// MARK: GET AlertContact
|
||||||
|
|
||||||
// GET without args -> Get all alertContacts
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A function to create a sortBy compatible object from a string
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @param {string} SortField
|
|
||||||
* @param {string} Order
|
|
||||||
* @returns {object}
|
|
||||||
*/
|
|
||||||
export function parseDynamicSortBy(SortField: string, Order: string) {
|
|
||||||
return JSON.parse(`{ "${SortField}": "${Order}" }`);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function to parse a string into a number or return undefined if it is not a number
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @param {string || any} data
|
|
||||||
* @returns {object}
|
|
||||||
*/
|
|
||||||
export function parseIntOrUndefined(data: any) {
|
|
||||||
return isNaN(parseInt(data)) ? undefined : parseInt(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// GET AlertContact
|
|
||||||
async function get(req: Request, res: Response) {
|
async function get(req: Request, res: Response) {
|
||||||
// Set sane defaults if undefined for sort
|
const { error, value } = schema_get.validate(req.query);
|
||||||
if (req.query.sort === undefined) {
|
if (error) {
|
||||||
req.query.sort = 'id';
|
log.api?.debug('Error:', req.query, value);
|
||||||
}
|
res.status(400).json({ error: error.details[0].message });
|
||||||
if (req.query.order === undefined) {
|
} else {
|
||||||
req.query.order = 'asc';
|
log.api?.debug('Success:', req.query, value);
|
||||||
}
|
|
||||||
|
|
||||||
// Prio 1 -> Get count (with or without filter)
|
// Query with FullTextSearch
|
||||||
// Prio 2 -> Get by id
|
const query_fts = {
|
||||||
// Prio 3 -> Get with filter
|
|
||||||
if ((req.query.search !== undefined && req.query.search.length > 0) || (req.query.id !== undefined && req.query.id.length > 0)) {
|
|
||||||
if (req.query.search !== undefined && req.query.search === '*') {
|
|
||||||
log.db.debug('Single * does not work with FullTextSearch');
|
|
||||||
req.query.search = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
// When an ID is set, remove(disable) the search query
|
|
||||||
if (req.query.id !== undefined && req.query.id.length > 0) {
|
|
||||||
req.query.search = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const query = {
|
|
||||||
where: {
|
where: {
|
||||||
OR: [{ id: parseIntOrUndefined(req.query.id) }, { name: { search: req.query.search } }, { phone: { search: req.query.search } }, { comment: { search: req.query.search } }]
|
OR: [{ id: parseIntOrUndefined(value.id) }, { name: { search: value.search } }, { phone: { search: value.search } }, { comment: { search: value.search } }]
|
||||||
},
|
},
|
||||||
orderBy: parseDynamicSortBy(req.query.sort.toString(), req.query.order.toString())
|
orderBy: parseDynamicSortBy(value.sort.toString(), value.order.toString())
|
||||||
};
|
};
|
||||||
|
|
||||||
if (req.query.count === undefined) {
|
if (value.search !== undefined || value.id !== undefined) {
|
||||||
// get all entrys
|
// with FullTextSearch
|
||||||
await db.alertContacts.findMany(query).then((result) => {
|
if (!value.count) {
|
||||||
res.status(200).json(result);
|
// get all entrys
|
||||||
});
|
log.api?.trace('get all entrys - with FullTextSearch');
|
||||||
|
await db.alertContacts.findMany(query_fts).then((result) => {
|
||||||
|
res.status(200).json(result);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// count all entrys
|
||||||
|
log.api?.trace('count all entrys - with FullTextSearch');
|
||||||
|
await db.alertContacts.count(query_fts).then((result) => {
|
||||||
|
res.status(200).json(result);
|
||||||
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// count all entrys (filtered or not)
|
// without FullTextSearch
|
||||||
await db.alertContacts.count(query).then((result) => {
|
if (!value.count) {
|
||||||
res.status(200).json(result);
|
// get all entrys
|
||||||
});
|
log.api?.trace('get all entrys - without FullTextSearch');
|
||||||
}
|
await db.alertContacts.findMany({ orderBy: parseDynamicSortBy(value.sort.toString(), value.order.toString()) }).then((result) => {
|
||||||
} else {
|
res.status(200).json(result);
|
||||||
if (req.query.count === undefined) {
|
});
|
||||||
await db.alertContacts.findMany({ orderBy: parseDynamicSortBy(req.query.sort.toString(), req.query.order.toString()) }).then((result) => {
|
} else {
|
||||||
res.status(200).json(result);
|
// count all entrys without FullTextSearch
|
||||||
});
|
log.api?.trace('count all entrys - without FullTextSearch');
|
||||||
} else {
|
await db.alertContacts.count().then((result) => {
|
||||||
await db.alertContacts.count().then((result) => {
|
res.status(200).json(result);
|
||||||
res.status(200).json(result);
|
});
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CREATE AlertContact
|
// MARK: CREATE AlertContact
|
||||||
async function post(req: Request, res: Response) {
|
async function post(req: Request, res: Response) {
|
||||||
|
|
||||||
// Check if undefined or null
|
// Check if undefined or null
|
||||||
if (req.body.name != null && req.body.phone != null) {
|
if (req.body.name != null && req.body.phone != null) {
|
||||||
await db.alertContacts
|
await db.alertContacts
|
||||||
.create({
|
.create({
|
||||||
data: {
|
data: {
|
||||||
name: req.body.name,
|
name: req.body.name,
|
||||||
phone: req.body.phone,
|
phone: req.body.phone,
|
||||||
comment: req.body.comment,
|
comment: req.body.comment
|
||||||
},
|
},
|
||||||
select: {
|
select: {
|
||||||
id: true
|
id: true
|
||||||
}
|
}
|
||||||
}).then((result) => {
|
})
|
||||||
res.status(201).json({ status: 'CREATED', message: 'Successfully created alertContact', id: result.id });
|
.then((result) => {
|
||||||
})
|
res.status(201).json({ status: 'CREATED', message: 'Successfully created alertContact', id: result.id });
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
res.status(400).json({ status: 'ERROR', errorcode: "VALIDATION_ERROR", message: 'One or more required fields are missing or invalid' });
|
res.status(400).json({ status: 'ERROR', errorcode: 'VALIDATION_ERROR', message: 'One or more required fields are missing or invalid' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// UPDATE AlertContact
|
// MARK: UPDATE AlertContact
|
||||||
async function patch(req: Request, res: Response) {}
|
async function patch(req: Request, res: Response) {}
|
||||||
|
|
||||||
// DELETE AlertContact
|
// MARK: DELETE AlertContact
|
||||||
async function del(req: Request, res: Response) {}
|
async function del(req: Request, res: Response) {}
|
||||||
|
|
||||||
export default { get, post, patch, del };
|
export default { get, post, patch, del };
|
||||||
|
53
src/routes/api/v1/alertContacts_schema.ts
Normal file
53
src/routes/api/v1/alertContacts_schema.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import { Request, Response } from 'express';
|
||||||
|
import validator from 'joi'; // DOCS: https://joi.dev/api
|
||||||
|
import log from '../../../handlers/log.js';
|
||||||
|
|
||||||
|
|
||||||
|
const schema_get = validator.object({
|
||||||
|
sort: validator.string().valid('id', 'name', 'phone', 'comment').default('id'),
|
||||||
|
order: validator.string().valid('asc', 'desc').default('asc'),
|
||||||
|
search: validator.string().min(3).max(20), // TODO: Check if * or ** or *** -> Due to crashes..
|
||||||
|
id: validator.number().positive().precision(0),
|
||||||
|
count: validator.boolean()
|
||||||
|
}).nand('id', 'search'); // Allow id or search. not both.
|
||||||
|
|
||||||
|
const schema_post = validator.object({
|
||||||
|
name: validator.string().min(1).max(32).required(),
|
||||||
|
phone: validator.string().pattern(new RegExp('^\\+(\\d{1,3})\\s*(?:\\(\\s*(\\d{2,5})\\s*\\)|\\s*(\\d{2,5})\\s*)\\s*(\\d{5,15})$')).required(),
|
||||||
|
comment: validator.string().max(64),
|
||||||
|
})
|
||||||
|
|
||||||
|
const schema_patch = validator.object({
|
||||||
|
sort: validator.string().valid('id', 'name', 'phone', 'comment').default('id'),
|
||||||
|
order: validator.string().valid('asc', 'desc').default('asc'),
|
||||||
|
search: validator.string().min(3).max(20), // TODO: Check if * or ** or *** -> Due to crashes..
|
||||||
|
id: validator.number().positive().precision(0),
|
||||||
|
count: validator.boolean()
|
||||||
|
}).nand('id', 'search'); // Allow id or search. not both.
|
||||||
|
|
||||||
|
const schema_del = validator.object({
|
||||||
|
sort: validator.string().valid('id', 'name', 'phone', 'comment').default('id'),
|
||||||
|
order: validator.string().valid('asc', 'desc').default('asc'),
|
||||||
|
search: validator.string().min(3).max(20), // TODO: Check if * or ** or *** -> Due to crashes..
|
||||||
|
id: validator.number().positive().precision(0),
|
||||||
|
count: validator.boolean()
|
||||||
|
}).nand('id', 'search'); // Allow id or search. not both.
|
||||||
|
|
||||||
|
// Describe all schemas
|
||||||
|
const schema_get_describe = schema_get.describe();
|
||||||
|
const schema_post_describe = schema_post.describe();
|
||||||
|
const schema_patch_describe = schema_patch.describe();
|
||||||
|
const schema_del_describe = schema_del.describe();
|
||||||
|
|
||||||
|
|
||||||
|
// GET route
|
||||||
|
export default async function get(req: Request, res: Response) {
|
||||||
|
res.status(200).json({
|
||||||
|
GET: schema_get_describe,
|
||||||
|
POST: schema_post_describe,
|
||||||
|
PATCH: schema_patch_describe,
|
||||||
|
DELETE: schema_del_describe
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export { schema_get, schema_post, schema_patch, schema_del }
|
@ -3,14 +3,11 @@ import passport from 'passport';
|
|||||||
|
|
||||||
// Route imports
|
// Route imports
|
||||||
import testRoute from './test.js';
|
import testRoute from './test.js';
|
||||||
import alertContactsRoute from './alertContacts.js';
|
|
||||||
//import categoryRoute from './categories.js';
|
|
||||||
//import storageUnitRoute from './storageUnits.js';
|
|
||||||
//import storageLocationRoute from './storageLocations.js';
|
|
||||||
//import contactInfo from './contactInfo.js';
|
|
||||||
import versionRoute from './version.js'
|
import versionRoute from './version.js'
|
||||||
|
|
||||||
//import search_routes from './search/index.js';
|
import alertContactsRoute from './alertContacts.js';
|
||||||
|
import alertContactsRoute_schema from './alertContacts_schema.js';
|
||||||
|
|
||||||
|
|
||||||
// Router base is '/api/v1'
|
// Router base is '/api/v1'
|
||||||
const Router = express.Router({ strict: false });
|
const Router = express.Router({ strict: false });
|
||||||
@ -27,11 +24,7 @@ Router.use('*', function (req, res, next) {
|
|||||||
|
|
||||||
// All api routes lowercase! Yea I know but when strict: true it matters.
|
// All api routes lowercase! Yea I know but when strict: true it matters.
|
||||||
Router.route('/alertcontacts').get(alertContactsRoute.get).post(alertContactsRoute.post).patch(alertContactsRoute.patch).delete(alertContactsRoute.del);
|
Router.route('/alertcontacts').get(alertContactsRoute.get).post(alertContactsRoute.post).patch(alertContactsRoute.patch).delete(alertContactsRoute.del);
|
||||||
//Router.route('/categories').get(categoryRoute.get).post(categoryRoute.post).patch(categoryRoute.patch).delete(categoryRoute.del);
|
Router.route('/alertcontacts/describe').get(alertContactsRoute_schema);
|
||||||
// TODO: Migrate routes to lowercase.
|
|
||||||
//Router.route('/storageUnits').get(storageUnitRoute.get).post(storageUnitRoute.post).patch(storageUnitRoute.patch).delete(storageUnitRoute.del);
|
|
||||||
//Router.route('/storageLocations').get(storageLocationRoute.get).post(storageLocationRoute.post).patch(storageLocationRoute.patch).delete(storageLocationRoute.del);
|
|
||||||
//Router.route('/contactInfo').get(contactInfo.get).post(contactInfo.post).patch(contactInfo.patch).delete(contactInfo.del);
|
|
||||||
|
|
||||||
Router.route('/version').get(versionRoute.get);
|
Router.route('/version').get(versionRoute.get);
|
||||||
//Router.use('/search', search_routes);
|
//Router.use('/search', search_routes);
|
||||||
|
Loading…
Reference in New Issue
Block a user