From b59134dc5e86a96a710d89e8fd5b05367fa5bd72 Mon Sep 17 00:00:00 2001 From: Ashelyn Dawn Date: Sun, 29 Mar 2020 15:21:40 -0600 Subject: [PATCH] Refactor validators --- api/auth.js | 13 +++--- api/cart.js | 13 +++--- api/categories.js | 15 +++---- api/items.js | 15 +++---- api/middleware/validators.js | 39 ++++++++++-------- api/users.js | 14 +++---- components/table/table.js | 8 ++-- components/table/table.module.css | 2 +- pages/store/cart.js | 67 ++++++++++++++++++++----------- 9 files changed, 100 insertions(+), 86 deletions(-) diff --git a/api/auth.js b/api/auth.js index 542aa7c..b6884f4 100644 --- a/api/auth.js +++ b/api/auth.js @@ -2,16 +2,15 @@ const router = require('express-promise-router')() const parseJSON = require('body-parser').json() const db = require('../db') -const {validationResult} = require('express-validator') const validate = require('./middleware/validators') -const loginValidation = [validate.emailRestrictions, validate.passwordRestrictions] -router.post('/', parseJSON, loginValidation, async (req, res) => { - const errors = validationResult(req) - - if(!errors.isEmpty()) - return res.status(422).json({errors: errors.array()}) +const loginValidation = [ + validate.validEmail('email'), + validate.validPassword('password'), + validate.handleApiError +] +router.post('/', parseJSON, loginValidation, async (req, res) => { const user = await db.user.login( req.body.email, req.body.password diff --git a/api/cart.js b/api/cart.js index 0062c4a..bab9d3c 100644 --- a/api/cart.js +++ b/api/cart.js @@ -2,20 +2,17 @@ const router = module.exports = require('express-promise-router')() const parseJSON = require('body-parser').json() const db = require('../db') -const {validationResult} = require('express-validator') const validate = require('./middleware/validators') router.get('/', async (req, res)=>{ res.json(req.cart); }) -const cartAddValidators = [validate.countInt] +const cartAddValidators = [ + validate.positiveInteger('count'), + validate.handleApiError +] router.post('/add/:item_uuid', parseJSON, cartAddValidators, async (req, res) => { - // Check form input - const errors = validationResult(req) - if(!errors.isEmpty()) - return res.status(422).json({errors: errors.array()}) - // Ensure session if(!req.session.uuid) await db.session.create(req) @@ -36,4 +33,4 @@ router.post('/remove/:item_uuid', async (req, res) => { const cart = await db.cart.removeItemFromCart(req.cart.uuid, req.params.item_uuid); res.json(cart) -}) \ No newline at end of file +}) diff --git a/api/categories.js b/api/categories.js index b61c41f..fc10d85 100644 --- a/api/categories.js +++ b/api/categories.js @@ -2,7 +2,6 @@ const router = module.exports = require('express-promise-router')() const parseJSON = require('body-parser').json() const db = require('../db') -const {validationResult} = require('express-validator') const validate = require('./middleware/validators') router.get('/', async (req, res) => { @@ -12,16 +11,12 @@ router.get('/', async (req, res) => { }) const newCategoryValidators = [ - validate.urlSlugRestrictions, - validate.nameRestrictions, - validate.descriptionRestrictions + validate.validUrlSlug('urlslug'), + validate.requiredString('name'), + validate.requiredString('description'), + validate.handleApiError ] router.post('/', parseJSON, newCategoryValidators, async (req, res) => { - const errors = validationResult(req) - - if(!errors.isEmpty()) - return res.status(422).json({errors: errors.array()}) - const category = await db.category.create( req.body.name, req.body.urlslug, @@ -39,4 +34,4 @@ router.put('/:category_uuid/items/:item_uuid', async (req, res) => { router.delete('/:category_uuid/items/:item_uuid', async (req, res) => { const category = await db.category.removeItem(req.params.category_uuid, req.params.item_uuid); res.json(category) -}) \ No newline at end of file +}) diff --git a/api/items.js b/api/items.js index 86272be..bdd7edc 100644 --- a/api/items.js +++ b/api/items.js @@ -2,7 +2,6 @@ const router = module.exports = require('express-promise-router')() const parseJSON = require('body-parser').json() const db = require('../db') -const {validationResult} = require('express-validator') const validate = require('./middleware/validators') const upload = require('multer')({ @@ -20,19 +19,15 @@ router.get('/', async (req, res) => { }) const newItemValidators = [ - validate.urlSlugRestrictions, + validate.validUrlSlug('urlslug'), validate.publishedBool, - validate.priceCentsInt, - validate.nameRestrictions, - validate.descriptionRestrictions + validate.positiveInteger('price_cents'), + validate.requiredString('name'), + validate.requiredString('description'), + validate.handleApiError ] router.post('/', parseJSON, newItemValidators, async (req, res) => { - const errors = validationResult(req) - - if(!errors.isEmpty()) - return res.status(422).json({errors: errors.array()}) - const item = await db.item.create( req.body.name, req.body.urlslug, diff --git a/api/middleware/validators.js b/api/middleware/validators.js index 23e3eb7..d680a27 100644 --- a/api/middleware/validators.js +++ b/api/middleware/validators.js @@ -1,9 +1,9 @@ -const {body} = require('express-validator') +const {body, validationResult} = require('express-validator') const db = require('../../db') const validators = module.exports = {} -validators.passwordRestrictions = body('password').isString().isLength({min: 8, max: 100}).withMessage('Password must be at least 8 characters') +validators.validPassword = field => body(field).isString().isLength({min: 8, max: 100}).withMessage('Password must be at least 8 characters') validators.bothPasswordsMatch = body('password').custom((pass, {req})=>{ if(pass !== req.body.password2) @@ -11,26 +11,33 @@ validators.bothPasswordsMatch = body('password').custom((pass, {req})=>{ return true }) -validators.emailRestrictions = body('email').isString().isEmail().withMessage('Email invalid') +validators.validEmail = field => body(field).isString().isEmail() + .withMessage('Email invalid') -validators.checkEmailNotUsed = body('email').custom(async email=>{ - const user = await db.user.findByEmail(email) - if(user) throw new Error('Email already in use') -}) +validators.unusedEmail = field => validators.validEmail(field) + .custom(async email=>{ + const user = await db.user.findByEmail(email) + if(user) throw new Error('Email already in use') + }) -validators.urlSlugRestrictions = body('urlslug').isString().isLength({min: 3, max: 20}).matches(/^[-a-z0-9_]*$/i) +validators.validUrlSlug = field => body(field).isString().isLength({min: 3, max: 20}).matches(/^[-a-z0-9_]*$/i) .withMessage('Slug can be between 3-20 characters long, and can include letters, numbers, dash or underscore') -validators.nameRestrictions = body('name').isString() - .withMessage('Name required. Must be a string') +validators.requiredString = field => body(field).isString() + .withMessage('Required') -validators.descriptionRestrictions = body('description').isString() - .withMessage('Description required. Must be a string') +validators.positiveInteger = field => body(field).isInt().custom(value => value > 0) + .withMessage('Must be a positive integer') validators.publishedBool = body('published').isBoolean() -validators.priceCentsInt = body('price_cents').isInt().custom(price => price > 0) - .withMessage('Price must be a positive integer') +validators.isUUID = field => body(field).isString().isUUID() + +validators.handleApiError = (req, res, next) => { + const errors = validationResult(req) + + if(errors.isEmpty()) + return next() -validators.countInt = body('count').isInt().custom(count => count > 0) - .withMessage('Count must be a positive integer') + res.status(422).json({errors: errors.array()}) +} diff --git a/api/users.js b/api/users.js index 96b2034..a4541b2 100644 --- a/api/users.js +++ b/api/users.js @@ -2,16 +2,16 @@ const router = require('express-promise-router')() const parseJSON = require('body-parser').json() const db = require('../db') -const {validationResult} = require('express-validator') const validate = require('./middleware/validators') -const registerValidation = [validate.emailRestrictions, validate.passwordRestrictions, validate.bothPasswordsMatch, validate.checkEmailNotUsed] -router.post('/', parseJSON, registerValidation, async (req, res) => { - const errors = validationResult(req) - - if(!errors.isEmpty()) - return res.status(422).json({errors: errors.array()}) +const registerValidation = [ + validate.unusedEmail('email'), + validate.validPassword('password'), + validate.bothPasswordsMatch, + validate.handleApiError +] +router.post('/', parseJSON, registerValidation, async (req, res) => { const user = await db.user.register( req.body.email, req.body.password diff --git a/components/table/table.js b/components/table/table.js index 8dee501..577ef52 100644 --- a/components/table/table.js +++ b/components/table/table.js @@ -10,15 +10,15 @@ export default function Table({columns, rows, foot}) { )} - + {rows && rows.length > 0 && {rows.map(row=> {columns.map(column=> - {column.extractor(row)} + {column.extractor(row)} )} )} - + } {foot && {foot.map((item, i) => @@ -30,4 +30,4 @@ export default function Table({columns, rows, foot}) { } ) -} \ No newline at end of file +} diff --git a/components/table/table.module.css b/components/table/table.module.css index ae4c23a..42b8ede 100644 --- a/components/table/table.module.css +++ b/components/table/table.module.css @@ -21,4 +21,4 @@ .table td { padding: 15px; -} \ No newline at end of file +} diff --git a/pages/store/cart.js b/pages/store/cart.js index a81775e..dfe8a9e 100644 --- a/pages/store/cart.js +++ b/pages/store/cart.js @@ -6,6 +6,8 @@ import {FormController, Input, Button} from '~/components/form' import Table from '~/components/table' export default function Cart({cart, setCart}){ + const numItems = (cart?.items) ? cart.items.length : 0 + const handleRemove = id => async ev => { if(ev) ev.preventDefault() @@ -13,36 +15,55 @@ export default function Cart({cart, setCart}){ setCart(data) } + const handleCreateTransaction = async () => { + + } + return ( <> Cart <>

Cart

- row.item.name}, - {name: 'Quantity in Cart', extractor: row => row.count}, - {name: 'Price Each', extractor: row => '$' + (row.item.price_cents / 100).toFixed(2)}, - {name: 'Total Price', extractor: row => '$' + (row.count * row.item.price_cents / 100).toFixed(2)}, - {name: '', extractor: row => - - } - ]} - rows={cart?.items?.map(row=>({ - ...row, - id: row.item.uuid - }))} - foot={[ - 'Total:', - cart?.items.map(r=>r.count).reduce((a,b) => (a+b), 0) || 0, - '', - '$' + ((cart?.items.map(r=>r.count * r.item.price_cents).reduce((a,b) => (a+b), 0) || 0) / 100).toFixed(2), - '' - ]} - /> + + { + numItems > 0 + ?
row.item.name}, + {name: 'Quantity in Cart', extractor: row => row.count}, + {name: 'Price Each', extractor: row => '$' + (row.item.price_cents / 100).toFixed(2)}, + {name: 'Total Price', extractor: row => '$' + (row.count * row.item.price_cents / 100).toFixed(2)}, + {name: '', extractor: row => + + } + ]} + rows={cart?.items?.map(row=>({ + ...row, + id: row.item.uuid + }))} + foot={[ + 'Total:', + cart?.items.map(r=>r.count).reduce((a,b) => (a+b), 0) || 0, + '', + '$' + ((cart?.items.map(r=>r.count * r.item.price_cents).reduce((a,b) => (a+b), 0) || 0) / 100).toFixed(2), + '' + ]} + /> + // Empty cart table + :
+ } -