diff --git a/api/accounts/accounts-middleware.js b/api/accounts/accounts-middleware.js index 240db4fe1..62b7f9b21 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..c64803da4 100644 --- a/api/accounts/accounts-router.js +++ b/api/accounts/accounts-router.js @@ -1,27 +1,67 @@ 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" },