From 8f582fd9d63b37fb9961e42d2b3891d9affbb6da Mon Sep 17 00:00:00 2001 From: Lirong Cui Date: Wed, 23 Feb 2022 23:52:55 -0800 Subject: [PATCH 1/3] setup --- queries.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/queries.sql b/queries.sql index 3058b19c5..f61ba34c9 100644 --- a/queries.sql +++ b/queries.sql @@ -2,6 +2,7 @@ -- Find all customers with postal code 1010 + -- Find the phone number for the supplier with the id 11 -- List first 10 orders placed, sorted descending by the order date From e7a644015d7e2ab4bd86541de83db2d493e82f32 Mon Sep 17 00:00:00 2001 From: Lirong Cui Date: Thu, 31 Mar 2022 21:05:37 -0700 Subject: [PATCH 2/3] wip --- api/accounts/accounts-middleware.js | 50 ++++++++++++++++++++++++-- api/accounts/accounts-model.js | 15 +++++--- api/accounts/accounts-router.js | 56 +++++++++++++++++++++++------ api/server.js | 10 ++++++ index.js | 2 +- package-lock.json | 42 ++++++++++++++++++++++ package.json | 1 + queries.sql | 1 - 8 files changed, 158 insertions(+), 19 deletions(-) diff --git a/api/accounts/accounts-middleware.js b/api/accounts/accounts-middleware.js index 240db4fe1..e15a082e4 100644 --- a/api/accounts/accounts-middleware.js +++ b/api/accounts/accounts-middleware.js @@ -1,13 +1,59 @@ +const Account = require('./accounts-model') + +const db = require('../../data/db-config') + exports.checkAccountPayload = (req, res, next) => { // DO YOUR MAGIC // Note: you can either write "manual" validation logic // or use the Yup library (not currently installed) + const error = { status: 400 } + const { name, budget } = req.body + if (name === undefined || budget === undefined) { + error.message = 'name and budget are required' + } else if (typeof name !== 'string') { + error.message = 'name of account must be a string' + } else if (name.trim().length < 3 || name.trim().length > 100) { + error.message = 'name of account must be between 3 and 100' + } else if (budget < 0 || budget > 1000000) { + error.message = 'budget of account is too large or too small' + } else if (typeof budget !== 'number' || !isNaN(budget)) { + error.message = 'budget of account must be a number' + } + + if (error.message) { + next(error) + } else { + next() + } } -exports.checkAccountNameUnique = (req, res, next) => { +exports.checkAccountNameUnique = async(req, res, next) => { // DO YOUR MAGIC + try { + const existing = await db('accounts') + .where('name', req.body.name.trim()) + .first() + if (existing) { + next({ status: 400, message: 'that name is taken' }) + } else { + next() + } + } catch (err) { + next(err) + } } -exports.checkAccountId = (req, res, next) => { +exports.checkAccountId = async (req, res, next) => { // DO YOUR MAGIC + try { + const account = await Account.getById(req.params.id) + if (!account) { + next({ status: 404, message: 'account not found'}) + } else { + req.account = account + next() + } + } catch (err) { + next(err) + } } diff --git a/api/accounts/accounts-model.js b/api/accounts/accounts-model.js index 7dccda30f..aa4895e25 100644 --- a/api/accounts/accounts-model.js +++ b/api/accounts/accounts-model.js @@ -1,21 +1,28 @@ +const db = require('../../data/db-config') + const getAll = () => { - // DO YOUR MAGIC + return db('accounts'); } const getById = id => { - // DO YOUR MAGIC + return db('accounts').where('id', id).first() } -const create = account => { +const create = async account => { // DO YOUR MAGIC + const [id] = await db('accounts').insert(account) + return getById(id) } -const updateById = (id, account) => { +const updateById = async (id, account) => { // DO YOUR MAGIC + await db('accounts').where('id', id).update(account) + return getById(id) } const deleteById = id => { // DO YOUR MAGIC + return db('accounts').where('id', id).del() } module.exports = { diff --git a/api/accounts/accounts-router.js b/api/accounts/accounts-router.js index 9e8cf07bb..cc16f0bee 100644 --- a/api/accounts/accounts-router.js +++ b/api/accounts/accounts-router.js @@ -1,27 +1,61 @@ const router = require('express').Router() -router.get('/', (req, res, next) => { - // DO YOUR MAGIC +const mw = require('./accounts-middleware') + +const Account = require('./accounts-model') + +router.get('/', async (req, res, next) => { + try { + const accounts = await Account.getAll() + res.json(accounts) + } catch (err) { + next(err) + } }) -router.get('/:id', (req, res, next) => { - // DO YOUR MAGIC +router.get('/:id', mw.checkAccountId, async (req, res, next) => { + try { + const account = await Account.getById(req.params.id) + res.json(account) + } catch (err) { + next(err) + } }) -router.post('/', (req, res, next) => { - // DO YOUR MAGIC +router.post('/', mw.checkAccountPayload, mw.checkAccountNameUnique, async(req, res, next) => { + try { + const newAccount = await Account.create({name: req.body.name.trim(), budget: req.body.budget}) + res.status(201).json(newAccount) + } catch (err) { + next(err) + } }) -router.put('/:id', (req, res, next) => { - // DO YOUR MAGIC +router.put('/:id', + mw.checkAccountId, + mw.checkAccountPayload, + async (req, res, next) => { + try { + const updated = await Account.updateById(req.params.id, req.body) + res.status(200).json(updated) + } catch (err) { + next(err) + } }); -router.delete('/:id', (req, res, next) => { - // DO YOUR MAGIC +router.delete('/:id', mw.checkAccountId, async (req, res, next) => { + try { + await Account.deleteById(req.params.id) + res.json(req.account) + } catch (err) { + next(err) + } }) router.use((err, req, res, next) => { // eslint-disable-line - // DO YOUR MAGIC + res.status(err.status || 500).json({ + message: err.message, + }) }) module.exports = router; diff --git a/api/server.js b/api/server.js index 5d25c9783..a51826e4c 100644 --- a/api/server.js +++ b/api/server.js @@ -1,7 +1,17 @@ const express = require("express"); +const accountsRouter = require('./accounts/accounts-router'); + const server = express(); server.use(express.json()); +server.use('/api/accounts', accountsRouter); + +server.use('*', (req, res) => { + res.status(404).json({ + message: 'error', + }) +}) + module.exports = server; diff --git a/index.js b/index.js index c91708571..52a7f42b5 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,6 @@ const server = require("./api/server.js"); -const PORT = process.env.PORT || 9000; +const PORT = process.env.PORT || 8080; server.listen(PORT, () => { console.log(`\n== API running on port ${PORT} ==\n`); diff --git a/package-lock.json b/package-lock.json index e4a7de4a7..5c595687a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "1.0.0", "dependencies": { "express": "^4.17.1", + "kill-port": "^1.6.1", "knex": "^0.95.14", "sqlite3": "^5.0.2" }, @@ -3181,6 +3182,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-them-args": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/get-them-args/-/get-them-args-1.3.2.tgz", + "integrity": "sha1-dKILqKSr7OWuGZrQPyvMaP38m6U=" + }, "node_modules/getopts": { "version": "2.2.5", "resolved": "https://registry.npmjs.org/getopts/-/getopts-2.2.5.tgz", @@ -4611,6 +4617,18 @@ "json-buffer": "3.0.0" } }, + "node_modules/kill-port": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/kill-port/-/kill-port-1.6.1.tgz", + "integrity": "sha512-un0Y55cOM7JKGaLnGja28T38tDDop0AQ8N0KlAdyh+B1nmMoX8AnNmqPNZbS3mUMgiST51DCVqmbFT1gNJpVNw==", + "dependencies": { + "get-them-args": "1.3.2", + "shell-exec": "1.0.2" + }, + "bin": { + "kill-port": "cli.js" + } + }, "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -6172,6 +6190,11 @@ "node": ">=8" } }, + "node_modules/shell-exec": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/shell-exec/-/shell-exec-1.0.2.tgz", + "integrity": "sha512-jyVd+kU2X+mWKMmGhx4fpWbPsjvD53k9ivqetutVW/BQ+WIZoDoP4d8vUMGezV6saZsiNoW2f9GIhg9Dondohg==" + }, "node_modules/side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -9705,6 +9728,11 @@ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true }, + "get-them-args": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/get-them-args/-/get-them-args-1.3.2.tgz", + "integrity": "sha1-dKILqKSr7OWuGZrQPyvMaP38m6U=" + }, "getopts": { "version": "2.2.5", "resolved": "https://registry.npmjs.org/getopts/-/getopts-2.2.5.tgz", @@ -10813,6 +10841,15 @@ "json-buffer": "3.0.0" } }, + "kill-port": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/kill-port/-/kill-port-1.6.1.tgz", + "integrity": "sha512-un0Y55cOM7JKGaLnGja28T38tDDop0AQ8N0KlAdyh+B1nmMoX8AnNmqPNZbS3mUMgiST51DCVqmbFT1gNJpVNw==", + "requires": { + "get-them-args": "1.3.2", + "shell-exec": "1.0.2" + } + }, "kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -12010,6 +12047,11 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, + "shell-exec": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/shell-exec/-/shell-exec-1.0.2.tgz", + "integrity": "sha512-jyVd+kU2X+mWKMmGhx4fpWbPsjvD53k9ivqetutVW/BQ+WIZoDoP4d8vUMGezV6saZsiNoW2f9GIhg9Dondohg==" + }, "side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", diff --git a/package.json b/package.json index 85c0c4950..f2477be94 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ }, "dependencies": { "express": "^4.17.1", + "kill-port": "^1.6.1", "knex": "^0.95.14", "sqlite3": "^5.0.2" }, diff --git a/queries.sql b/queries.sql index f61ba34c9..3058b19c5 100644 --- a/queries.sql +++ b/queries.sql @@ -2,7 +2,6 @@ -- Find all customers with postal code 1010 - -- Find the phone number for the supplier with the id 11 -- List first 10 orders placed, sorted descending by the order date From 48a0f8cc36badc688d7c5ab6ddc13bcaf6570522 Mon Sep 17 00:00:00 2001 From: Lirong Cui Date: Thu, 31 Mar 2022 22:11:45 -0700 Subject: [PATCH 3/3] mvp --- api/accounts/accounts-middleware.js | 2 +- api/accounts/accounts-router.js | 20 +++++++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/api/accounts/accounts-middleware.js b/api/accounts/accounts-middleware.js index e15a082e4..62b7f9b21 100644 --- a/api/accounts/accounts-middleware.js +++ b/api/accounts/accounts-middleware.js @@ -16,7 +16,7 @@ exports.checkAccountPayload = (req, res, next) => { error.message = 'name of account must be between 3 and 100' } else if (budget < 0 || budget > 1000000) { error.message = 'budget of account is too large or too small' - } else if (typeof budget !== 'number' || !isNaN(budget)) { + } else if (typeof budget !== 'number' || isNaN(budget)) { error.message = 'budget of account must be a number' } diff --git a/api/accounts/accounts-router.js b/api/accounts/accounts-router.js index cc16f0bee..c64803da4 100644 --- a/api/accounts/accounts-router.js +++ b/api/accounts/accounts-router.js @@ -22,13 +22,19 @@ router.get('/:id', mw.checkAccountId, async (req, res, next) => { } }) -router.post('/', mw.checkAccountPayload, mw.checkAccountNameUnique, async(req, res, next) => { - try { - const newAccount = await Account.create({name: req.body.name.trim(), budget: req.body.budget}) - res.status(201).json(newAccount) - } catch (err) { - next(err) - } +router.post('/', + mw.checkAccountPayload, + mw.checkAccountNameUnique, + async (req, res, next) => { + try { + const newAccount = await Account.create({ + name: req.body.name.trim(), + budget: req.body.budget, + }) + res.status(201).json(newAccount) + } catch (err) { + next(err) + } }) router.put('/:id',