diff --git a/api/cars/cars-middleware.js b/api/cars/cars-middleware.js index 7e738d6062..0bf61d4b94 100644 --- a/api/cars/cars-middleware.js +++ b/api/cars/cars-middleware.js @@ -1,15 +1,76 @@ -const checkCarId = (req, res, next) => { - // DO YOUR MAGIC +const Car = require('./cars-model') +const vin = require('vin-validator') + +const checkCarId = async (req, res, next) => { + try{ + const car = await Car.getById(req.params.id) + if(!car){ + next({ + status: 404, + message: `car with id ${car} is not found` + }) + }else{ + req.car = car + next() + } + }catch(err){ + next(err) + } } const checkCarPayload = (req, res, next) => { - // DO YOUR MAGIC + if(!req.body.vin) + return next({ + status: 400, + message: 'vin is missing' + }) + if(!req.body.make) + return next({ + status: 400, + message: 'make is missing' + }) + if(!req.body.model) + return next({ + status: 400, + message: 'model is missing' + }) + if(!req.body.mileage) + return next({ + status: 400, + message: 'mileage is missing' + }) + next() } const checkVinNumberValid = (req, res, next) => { - // DO YOUR MAGIC + if(vin.validate(req.body.vin)){ + next() + }else{ + next({ + status: 400, + message: `vin ${req.body.vin} is invalid` + }) + } } -const checkVinNumberUnique = (req, res, next) => { - // DO YOUR MAGIC +const checkVinNumberUnique = async (req, res, next) => { + try{ + const existingVin = await Car.getByVin(req.body.vin) + if(!existingVin){ + next() + }else{ + next({ + status: 400, + message: `vin ${req.body.vin} already exists` + }) + } + }catch(err){ + next(err) + } } +module.exports = { + checkCarId, + checkCarPayload, + checkVinNumberUnique, + checkVinNumberValid, +} \ No newline at end of file diff --git a/api/cars/cars-model.js b/api/cars/cars-model.js index 39b2c50a8d..3126bb3570 100644 --- a/api/cars/cars-model.js +++ b/api/cars/cars-model.js @@ -1,11 +1,37 @@ +const db = require('../../data/db-config') + const getAll = () => { - // DO YOUR MAGIC + return db('cars') +} + +const getById = (id) => { + return db('cars').where('id', id).first() +} + +const getByVin = (vin) => { + return db('cars').where('vin', vin).first() +} + +const create = (car) => { + return db('cars').insert(car).then(([id]) => { + return getById(id) + }) +} + +const updateById = (id, car) => { + db('cars').where('id', id).update(car) + return getById(id) } -const getById = () => { - // DO YOUR MAGIC +const deleteById = (id) => { + return db('cars').where('id', id).del() } -const create = () => { - // DO YOUR MAGIC +module.exports = { + getAll, + getByVin, + getById, + create, + updateById, + deleteById } diff --git a/api/cars/cars-router.js b/api/cars/cars-router.js index 3fca0e7d7d..eb2ef74861 100644 --- a/api/cars/cars-router.js +++ b/api/cars/cars-router.js @@ -1 +1,64 @@ -// DO YOUR MAGIC +const router = require('express').Router() +const Car = require('./cars-model') +const { + checkCarId, + checkVinNumberUnique, + checkCarPayload, + checkVinNumberValid, +} = require('./cars-middleware') + +router.get('/', async (req, res, next) => { + try{ + const cars = await Car.getAll() + res.json(cars) + }catch(err){ + next(err) + } +}) + +router.get('/:id', checkCarId, (req, res, next) => { + res.json(req.car) +}) + +router.post('/', +checkCarPayload, +checkVinNumberUnique, +checkVinNumberValid, +async (req, res, next) => { + try{ + const newCar = await Car.create(req.body) + res.status(201).json(newCar) + }catch(err){ + next(err) + } +}) + +router.put('/:id', +checkCarId, +checkCarPayload, +async (req, res, next) => { + try{ + const updatedCar = await Car.updateById(req.params.id, req.body) + res.status(200).json(updatedCar) + }catch(err){ + next(err) + } +}) + +router.delete('/:id', checkCarId, (req, res, next) => { + Car.deleteById(req.params.id) + .then(deletedCar => { + res.status(200).json(deletedCar) + }) + .catch(err => { + next(err) + }) +}) + +router.use((err, req, res, next) => { + res.status(err.status || 500).json({ + message: err.message, + stack: err.stack, + }); + }); +module.exports = router \ No newline at end of file diff --git a/api/server.js b/api/server.js index c5a0be1b41..071b45d969 100644 --- a/api/server.js +++ b/api/server.js @@ -1,7 +1,19 @@ const express = require("express") - +const carsRouter = require('./cars/cars-router') const server = express() -// DO YOUR MAGIC +server.use(express.json()) +server.use('/api/cars', carsRouter) +server.use('*', (req, res, next) => { + next({ + status: 404, + message: 'not found' + }) +}) +server.use((err, req, res, next) => { + res.status(err.status || 500).json({ + message: err.message + }) +}) module.exports = server diff --git a/data/dealer.db3 b/data/dealer.db3 new file mode 100644 index 0000000000..8d1d5c79aa Binary files /dev/null and b/data/dealer.db3 differ diff --git a/data/migrations/01-make_cars_table.js b/data/migrations/01-make_cars_table.js index 9723c175be..b10625aabb 100644 --- a/data/migrations/01-make_cars_table.js +++ b/data/migrations/01-make_cars_table.js @@ -1,7 +1,15 @@ exports.up = function (knex) { - // DO YOUR MAGIC + return knex.schema.createTable('cars', (table) => { + table.increments() + table.string('vin', 16).notNullable().unique() + table.string('make', 128).notNullable() + table.string('model', 128).notNullable() + table.integer('mileage', 128).notNullable().unsigned() + table.string('title', 128) + table.string('transmission', 128) + }) }; exports.down = function (knex) { - // DO YOUR MAGIC + return knex.schema.dropTableIfExists('cars') }; diff --git a/data/seeds/01-cars.js b/data/seeds/01-cars.js index 3f7acdde23..f7f2929e46 100644 --- a/data/seeds/01-cars.js +++ b/data/seeds/01-cars.js @@ -1 +1,31 @@ -// STRETCH +const cars = [ + { + vin: 'JTEBU11F670058710', + make: 'toyota', + model: 'yaris', + mileage: 235000, + title: 'clean', + transmission: 'manual' + }, + { + vin: '1D7HA18287S191814', + make: 'jeep', + model: 'grand cherokee', + mileage: 115000, + title: 'clean', + transmission: 'automatic' + }, + { + vin: '5UXXW3C58F0M65560', + make: 'ford', + model: 'bronco', + mileage: 250000, + title: 'clean', + transmission: 'automatic' + } + ] + + exports.seed = async function(knex){ + await knex('cars').truncate() + await knex('cars').insert(cars) + } diff --git a/package-lock.json b/package-lock.json index d1f3f92341..3f43fe209f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,12 +5,12 @@ "requires": true, "packages": { "": { - "name": "node-db2-project", "version": "1.0.0", "dependencies": { "express": "^4.17.1", - "knex": "^0.95.14", - "sqlite3": "^5.0.2" + "knex": "^0.95.15", + "sqlite3": "^5.0.2", + "vin-validator": "^1.0.0" }, "devDependencies": { "@types/jest": "^27.0.3", @@ -4614,9 +4614,10 @@ } }, "node_modules/knex": { - "version": "0.95.14", - "resolved": "https://registry.npmjs.org/knex/-/knex-0.95.14.tgz", - "integrity": "sha512-j4qLjWySrC/JRRVtOpoR2LcS1yBOsd7Krc6mEukPvmTDX/w11pD52Pq9FYR56/kLXGeAV8jFdWBjsZFi1mscWg==", + "version": "0.95.15", + "resolved": "https://registry.npmjs.org/knex/-/knex-0.95.15.tgz", + "integrity": "sha512-Loq6WgHaWlmL2bfZGWPsy4l8xw4pOE+tmLGkPG0auBppxpI0UcK+GYCycJcqz9W54f2LiGewkCVLBm3Wq4ur/w==", + "license": "MIT", "dependencies": { "colorette": "2.0.16", "commander": "^7.1.0", @@ -6882,6 +6883,11 @@ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "optional": true }, + "node_modules/vin-validator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/vin-validator/-/vin-validator-1.0.0.tgz", + "integrity": "sha1-F2Via4VYskAvd7bCXHYX9zzsrmc=" + }, "node_modules/w3c-hr-time": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", @@ -10785,9 +10791,9 @@ "dev": true }, "knex": { - "version": "0.95.14", - "resolved": "https://registry.npmjs.org/knex/-/knex-0.95.14.tgz", - "integrity": "sha512-j4qLjWySrC/JRRVtOpoR2LcS1yBOsd7Krc6mEukPvmTDX/w11pD52Pq9FYR56/kLXGeAV8jFdWBjsZFi1mscWg==", + "version": "0.95.15", + "resolved": "https://registry.npmjs.org/knex/-/knex-0.95.15.tgz", + "integrity": "sha512-Loq6WgHaWlmL2bfZGWPsy4l8xw4pOE+tmLGkPG0auBppxpI0UcK+GYCycJcqz9W54f2LiGewkCVLBm3Wq4ur/w==", "requires": { "colorette": "2.0.16", "commander": "^7.1.0", @@ -12518,6 +12524,11 @@ } } }, + "vin-validator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/vin-validator/-/vin-validator-1.0.0.tgz", + "integrity": "sha1-F2Via4VYskAvd7bCXHYX9zzsrmc=" + }, "w3c-hr-time": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", diff --git a/package.json b/package.json index cdbfc72774..f71feffcb4 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,14 @@ "version": "1.0.0", "main": "index.js", "scripts": { + "start": "node index.js", "server": "nodemon index.js", - "test": "cross-env NODE_ENV=testing jest --verbose --runInBand" + "test": "cross-env NODE_ENV=testing jest --verbose --runInBand", + "migrate": "knex migrate:latest", + "rollback": "knex migrate:rollback", + "migup": "knex migrate:up", + "migdown": "knex migrate:down", + "seed": "knex seed:run" }, "devDependencies": { "@types/jest": "^27.0.3", @@ -16,8 +22,9 @@ }, "dependencies": { "express": "^4.17.1", - "knex": "^0.95.14", - "sqlite3": "^5.0.2" + "knex": "^0.95.15", + "sqlite3": "^5.0.2", + "vin-validator": "^1.0.0" }, "repository": { "type": "git",