From db6366550dad3b2af5ff983f7517a1d6cd6f0b0b Mon Sep 17 00:00:00 2001 From: Ashelyn Dawn Date: Tue, 25 Feb 2020 21:01:22 -0700 Subject: [PATCH] API can add and view items/images --- api/images.js | 11 ++++++++++ api/index.js | 2 ++ api/items.js | 41 ++++++++++++++++++++++++++++-------- api/middleware/validators.js | 13 +++++++++++- db/index.js | 3 ++- db/mappings/index.js | 3 ++- db/mappings/item.js | 2 +- db/models/item.js | 19 ++++++++++++----- db/sql/1-tables.sql | 2 +- db/sql/3-functions.sql | 12 +++++------ index.js | 1 + 11 files changed, 84 insertions(+), 25 deletions(-) create mode 100644 api/images.js diff --git a/api/images.js b/api/images.js new file mode 100644 index 0000000..4136e57 --- /dev/null +++ b/api/images.js @@ -0,0 +1,11 @@ +const router = require('express-promise-router')() +const db = require('../db') + +router.get('/:uuid/:size', async (req, res) => { + const image = await db.item.getImage(req.params.uuid, req.params.size) + + res.set('Content-Type', image.mime_type) + res.end(image.file) +}) + +module.exports = router \ No newline at end of file diff --git a/api/index.js b/api/index.js index 43be98d..6230de8 100644 --- a/api/index.js +++ b/api/index.js @@ -2,6 +2,8 @@ const router = require('express-promise-router')() const pg = require('../db/pg') router.use('/users/', require('./users')) +router.use('/items/', require('./items')) +router.use('/images/', require('./images')) router.get('/', (req, res)=>{ res.json({test: true}) diff --git a/api/items.js b/api/items.js index 9889128..9dde586 100644 --- a/api/items.js +++ b/api/items.js @@ -5,34 +5,57 @@ const db = require('../db') const {validationResult} = require('express-validator') const validate = require('./middleware/validators') -const upload = multer({ - storage: multer.memoryStorage(), +const upload = require('multer')({ + storage: require('multer').memoryStorage(), limits: { files: 1, fileSize: 500000 } }) -router.post('/', parseJSON, validate.urlSlugRestrictions('slug'), async (req, res) => { +router.get('/', async (req, res) => { + const items = await db.item.findAll() + + res.json(items) +}) + +const newItemValidators = [ + validate.urlSlugRestrictions, + validate.publishedBool, + validate.priceCentsInt, + validate.nameRestrictions, + validate.descriptionRestrictions +] + +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, + req.body.description, + req.body.price_cents, + req.body.published + ) - // TODO: Create session - - res.json(user) + res.json(item) }) router.post('/:uuid/images', upload.single('image'), async (req, res) => { + // TODO: Use the real user when we have authentication + req.user = { + uuid: '5e17388d-612b-4ea8-b76d-620a7bb24824' + } + // Handle either image upload body or JSON body try { if(req.file) - await db.item.addImage(req.params.uuid, req.file.buffer) + await db.item.addImage(req.params.uuid, req.file.buffer, req.user.uuid) else - await db.item.addImage(req.params.uuid, await b64.decode(req.body.image.split(',')[1])) + await db.item.addImage(req.params.uuid, await b64.decode(req.body.image.split(',')[1]), req.user.uuid) } catch (error) { error.status = 500 throw error diff --git a/api/middleware/validators.js b/api/middleware/validators.js index b39a007..e837a18 100644 --- a/api/middleware/validators.js +++ b/api/middleware/validators.js @@ -18,5 +18,16 @@ validators.checkEmailNotUsed = body('email').custom(async email=>{ if(user) throw new Error('Email already in use') }) -validators.urlSlugRestrictions = field => body(field).isString().isLength({min: 3, max: 20}).matches(/[-a-z0-9_]*/i) +validators.urlSlugRestrictions = body('urlslug').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.descriptionRestrictions = body('description').isString() + .withMessage('Description required. Must be a string') + +validators.publishedBool = body('published').isBoolean() + +validators.priceCentsInt = body('price_cents').isInt().custom(price => price > 0) + .withMessage('Price must be a positive integer') \ No newline at end of file diff --git a/db/index.js b/db/index.js index 89ffc32..26bda6d 100644 --- a/db/index.js +++ b/db/index.js @@ -1,3 +1,4 @@ module.exports = { - user: require('./models/user') + user: require('./models/user'), + item: require('./models/item') } \ No newline at end of file diff --git a/db/mappings/index.js b/db/mappings/index.js index b403e28..9081c50 100644 --- a/db/mappings/index.js +++ b/db/mappings/index.js @@ -1,3 +1,4 @@ module.exports = [ - ...require('./user') + ...require('./user'), + ...require('./item') ] \ No newline at end of file diff --git a/db/mappings/item.js b/db/mappings/item.js index 4c7f7a9..f8a2f24 100644 --- a/db/mappings/item.js +++ b/db/mappings/item.js @@ -22,7 +22,7 @@ module.exports = [{ 'published' ], collections: [ - {name: 'images', mapId: 'imageMap', columnPrefix: 'imageColumn_'} + {name: 'images', mapId: 'imageMap', columnPrefix: 'image_'} ] },{ mapId: 'bareImageMap', diff --git a/db/models/item.js b/db/models/item.js index d2b82e0..57aa78f 100644 --- a/db/models/item.js +++ b/db/models/item.js @@ -7,6 +7,15 @@ const sharp = require('sharp') const item = module.exports = {} +item.findAll = async () => { + const query = 'select * from v_item' + + debug(query); + + const {rows} = await pg.query(query) + return joinjs.map(rows, mappings, 'itemMap', 'item_'); +} + item.findById = async (item_uuid) => { const query = { text: 'select * from v_item where item_uuid = $1', @@ -37,7 +46,7 @@ item.findBySlug = async (item_slug) => { item.create = async (name, urlslug, description, price_cents, published) => { const query = { - text: 'select * from public.create_item($1::text, $2::citext, $2::text, $4::integer, $5::boolean)', + text: 'select * from public.create_item($1::text, $2::citext, $3::text, $4::integer, $5::boolean)', values: [ name, urlslug, @@ -80,20 +89,20 @@ item.addImage = async (item_uuid, image_buffer, uploader_uuid) => { return joinjs.map(rows, mappings, 'itemMap', 'item_') } -const queries = { +const imageSizeQueries = { 'large': 'select * from get_image_large($1)', 'thumb': 'select * from get_image_thumb($1)' } item.getImage = async (image_uuid, size) => { - if(!profileSizeQueries[size]) + if(!imageSizeQueries[size]) throw new Error(`Cannot get unknown image size: "${size}"`) const query = { - text: profileSizeQueries[size], + text: imageSizeQueries[size], values: [image_uuid] } const {rows} = await pg.query(query) - return joinjs.map(rows, relationMaps, 'bareImageMap', 'image_')[0]; + return joinjs.map(rows, mappings, 'bareImageMap', 'image_')[0]; } \ No newline at end of file diff --git a/db/sql/1-tables.sql b/db/sql/1-tables.sql index bbb574d..747acd6 100644 --- a/db/sql/1-tables.sql +++ b/db/sql/1-tables.sql @@ -51,7 +51,7 @@ create table "cart_item" ( cart_item_uuid uuid primary key default uuid_generate_v4(), cart_item_item_uuid uuid not null references "item" (item_uuid), cart_item_cart_uuid uuid not null references "cart" (cart_uuid), - cart_item_count integer not null default 1, + cart_item_count integer not null default 1 check (cart_item_count > 0), cart_item_time_added timestamptz not null default now() ); diff --git a/db/sql/3-functions.sql b/db/sql/3-functions.sql index aee7064..a1c14f6 100644 --- a/db/sql/3-functions.sql +++ b/db/sql/3-functions.sql @@ -123,9 +123,9 @@ create or replace function public.get_image_large(_image_uuid uuid) as $function$ begin return query select - image_uuid, - image_mime_type, - image_large_file as image_file + "image".image_uuid, + "image".image_mime_type, + "image".image_large_file as image_file from "image" where "image".image_uuid = _image_uuid; end; $function$; @@ -136,9 +136,9 @@ create or replace function public.get_image_thumb(_image_uuid uuid) as $function$ begin return query select - image_uuid, - image_mime_type, - image_large_thumb as image_file + "image".image_uuid, + "image".image_mime_type, + "image".image_thumb_file as image_file from "image" where "image".image_uuid = _image_uuid; end; $function$; diff --git a/index.js b/index.js index 7c8ff6e..68f09d2 100644 --- a/index.js +++ b/index.js @@ -1,3 +1,4 @@ +require('dotenv').config() const express = require('express'); const next = require('next');