diff --git a/api/cars/cars-middleware.js b/api/cars/cars-middleware.js index 7e738d6062..3184723343 100644 --- a/api/cars/cars-middleware.js +++ b/api/cars/cars-middleware.js @@ -1,15 +1,72 @@ -const checkCarId = (req, res, next) => { - // DO YOUR MAGIC +const Cars = require('./cars-model') +const db = require('../../data/db-config') +const vinValidator = require('vin-validator') +const yup = require('yup') + +const checkCarId = async (req, res, next) => { + try { + const carIdCheck = await Cars.getById(req.params.id) + if (carIdCheck) { + req.car = carIdCheck + next() + } else { + res.status(404).json({ + message: `car with id ${req.params.id} is not found` + }) + } + } catch (err) { + next(err) + } } -const checkCarPayload = (req, res, next) => { - // DO YOUR MAGIC +const chechCarPayloadSchema = yup.object().shape({ + vin: yup.string().required('vin is missing'), + make: yup.string().required('make is missing'), + model: yup.string().required('model is missing'), + mileage: yup.number().required('mileage is missing'), + title: yup.string(), + transmission: yup.string() +}) + +const checkCarPayload = async (req, res, next) => { + try { + const payloadValidation = await chechCarPayloadSchema.validate( + req.body, + { strict: false, stripUnknown: true } + ) + req.body = payloadValidation + next() + } catch (err) { + res.status(400).json({ + message: err.message + }) + } } const checkVinNumberValid = (req, res, next) => { - // DO YOUR MAGIC + next() +} + +const checkVinNumberUnique = async (req, res, next) => { + try { + const exisiting = await db('cars') + .where('vin', req.body.vin) + .first() + if (!exisiting) { + next() + } else { + res.status(400).json({ + message: `vin ${req.body.vin} already exists` + }) + } + } catch (err) { + next(err) + } } -const checkVinNumberUnique = (req, res, next) => { - // DO YOUR MAGIC +module.exports = { + checkCarId, + checkCarPayload, + checkVinNumberValid, + checkVinNumberUnique, } diff --git a/api/cars/cars-model.js b/api/cars/cars-model.js index 39b2c50a8d..e2561f05c5 100644 --- a/api/cars/cars-model.js +++ b/api/cars/cars-model.js @@ -1,11 +1,25 @@ + +const db = require('../../data/db-config') + const getAll = () => { - // DO YOUR MAGIC + return db('cars') } -const getById = () => { - // DO YOUR MAGIC +const getById = (id) => { + return db('cars') + .where('id', id) + .first() } -const create = () => { - // DO YOUR MAGIC +const create = (car) => { + return db('cars').insert(car) + .then(([id]) => { + return getById(id) + }) } + +module.exports = { + getAll, + getById, + create, +} \ No newline at end of file diff --git a/api/cars/cars-router.js b/api/cars/cars-router.js index 3fca0e7d7d..ebf6682d4d 100644 --- a/api/cars/cars-router.js +++ b/api/cars/cars-router.js @@ -1 +1,35 @@ -// DO YOUR MAGIC +const router = require('express').Router() +const Cars = require('./cars-model') +const { checkCarId, + checkCarPayload, + checkVinNumberValid, + checkVinNumberUnique, +} = require('./cars-middleware') + +router.get('/', async (req, res, next) => { + try { + const cars = await Cars.getAll() + res.json(cars) + } catch (err) { + next(err) + } +}) + +router.get('/:id', checkCarId, async (req, res) => { + res.status(200).json(req.car) +}) + +router.post('/', +checkCarPayload, +checkVinNumberValid, +checkVinNumberUnique, +async (req, res, next) => { + try { + const newCar = await Cars.create(req.body) + res.json(newCar) + } catch (err) { + next(err) + } +}) + +module.exports = router \ No newline at end of file diff --git a/api/server.js b/api/server.js index c5a0be1b41..64fe434f5f 100644 --- a/api/server.js +++ b/api/server.js @@ -1,7 +1,14 @@ const express = require("express") - +const CarRouter = require('./cars/cars-router'); const server = express() -// DO YOUR MAGIC +server.use(express.json()); +server.use('/api/cars', CarRouter); + +server.get('/', (req, res) => { + res.send(`

CARS

`) +}); + + -module.exports = server +module.exports = server \ No newline at end of file diff --git a/data/dealer.db3 b/data/dealer.db3 new file mode 100644 index 0000000000..c4ca72f96d 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..3adfbc20f3 100644 --- a/data/migrations/01-make_cars_table.js +++ b/data/migrations/01-make_cars_table.js @@ -1,7 +1,17 @@ exports.up = function (knex) { // DO YOUR MAGIC + return knex.schema.createTable('cars', tbl => { + tbl.increments(); + tbl.string('vin').unique().notNullable(); + tbl.string('make').notNullable(); + tbl.string('model').notNullable(); + tbl.integer('mileage').notNullable(); + tbl.string('title'); + tbl.string('transmission'); + }) }; exports.down = function (knex) { // DO YOUR MAGIC + return knex.schema.dropTableIfExists('cars'); }; diff --git a/package-lock.json b/package-lock.json index 4db3b8fc7b..8c42c45b87 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,12 +5,13 @@ "requires": true, "packages": { "": { - "name": "node-db2-project", "version": "1.0.0", "dependencies": { "express": "^4.17.1", "knex": "^0.95.11", - "sqlite3": "^5.0.2" + "sqlite3": "^5.0.2", + "vin-validator": "^1.0.0", + "yup": "^0.32.11" }, "devDependencies": { "@types/jest": "^27.0.1", @@ -566,6 +567,17 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/runtime": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.15.4.tgz", + "integrity": "sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw==", + "dependencies": { + "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.15.4", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.15.4.tgz", @@ -1101,6 +1113,11 @@ "pretty-format": "^27.0.0" } }, + "node_modules/@types/lodash": { + "version": "4.14.175", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.175.tgz", + "integrity": "sha512-XmdEOrKQ8a1Y/yxQFOMbC47G/V2VDO1GvMRnl4O75M4GW/abC5tnfzadQYkqEveqRM1dEJGFFegfPNA2vvx2iw==" + }, "node_modules/@types/node": { "version": "16.7.13", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.13.tgz", @@ -5577,6 +5594,11 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, "node_modules/lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", @@ -5825,6 +5847,11 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/nanoclone": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/nanoclone/-/nanoclone-0.2.1.tgz", + "integrity": "sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==" + }, "node_modules/nanomatch": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", @@ -6806,6 +6833,11 @@ "node": ">= 6" } }, + "node_modules/property-expr": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.4.tgz", + "integrity": "sha512-sFPkHQjVKheDNnPvotjQmm3KD3uk1fWKUN7CrpdbwmUx3CrG3QiM8QpTSimvig5vTXmTvjz7+TDvXOI9+4rkcg==" + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -7006,6 +7038,11 @@ "node": ">= 0.10" } }, + "node_modules/regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + }, "node_modules/regex-not": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", @@ -8727,6 +8764,11 @@ "node": ">=0.6" } }, + "node_modules/toposort": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", + "integrity": "sha1-riF2gXXRVZ1IvvNUILL0li8JwzA=" + }, "node_modules/touch": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", @@ -9144,6 +9186,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", @@ -9468,6 +9515,23 @@ "engines": { "node": ">=8" } + }, + "node_modules/yup": { + "version": "0.32.11", + "resolved": "https://registry.npmjs.org/yup/-/yup-0.32.11.tgz", + "integrity": "sha512-Z2Fe1bn+eLstG8DRR6FTavGD+MeAwyfmouhHsIUgaADz8jvFKbO/fXc2trJKZg+5EBjh4gGm3iU/t3onKlXHIg==", + "dependencies": { + "@babel/runtime": "^7.15.4", + "@types/lodash": "^4.14.175", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "nanoclone": "^0.2.1", + "property-expr": "^2.0.4", + "toposort": "^2.0.2" + }, + "engines": { + "node": ">=10" + } } }, "dependencies": { @@ -9885,6 +9949,14 @@ "@babel/helper-plugin-utils": "^7.14.5" } }, + "@babel/runtime": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.15.4.tgz", + "integrity": "sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, "@babel/template": { "version": "7.15.4", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.15.4.tgz", @@ -10343,6 +10415,11 @@ "pretty-format": "^27.0.0" } }, + "@types/lodash": { + "version": "4.14.175", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.175.tgz", + "integrity": "sha512-XmdEOrKQ8a1Y/yxQFOMbC47G/V2VDO1GvMRnl4O75M4GW/abC5tnfzadQYkqEveqRM1dEJGFFegfPNA2vvx2iw==" + }, "@types/node": { "version": "16.7.13", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.13.tgz", @@ -13861,6 +13938,11 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, "lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", @@ -14056,6 +14138,11 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "nanoclone": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/nanoclone/-/nanoclone-0.2.1.tgz", + "integrity": "sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==" + }, "nanomatch": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", @@ -14810,6 +14897,11 @@ "sisteransi": "^1.0.5" } }, + "property-expr": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.4.tgz", + "integrity": "sha512-sFPkHQjVKheDNnPvotjQmm3KD3uk1fWKUN7CrpdbwmUx3CrG3QiM8QpTSimvig5vTXmTvjz7+TDvXOI9+4rkcg==" + }, "proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -14971,6 +15063,11 @@ "resolve": "^1.9.0" } }, + "regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + }, "regex-not": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", @@ -16337,6 +16434,11 @@ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" }, + "toposort": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", + "integrity": "sha1-riF2gXXRVZ1IvvNUILL0li8JwzA=" + }, "touch": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", @@ -16676,6 +16778,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", @@ -16941,6 +17048,20 @@ "camelcase": "^5.0.0", "decamelize": "^1.2.0" } + }, + "yup": { + "version": "0.32.11", + "resolved": "https://registry.npmjs.org/yup/-/yup-0.32.11.tgz", + "integrity": "sha512-Z2Fe1bn+eLstG8DRR6FTavGD+MeAwyfmouhHsIUgaADz8jvFKbO/fXc2trJKZg+5EBjh4gGm3iU/t3onKlXHIg==", + "requires": { + "@babel/runtime": "^7.15.4", + "@types/lodash": "^4.14.175", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "nanoclone": "^0.2.1", + "property-expr": "^2.0.4", + "toposort": "^2.0.2" + } } } } diff --git a/package.json b/package.json index 09c4d0ea1e..58dc4383e1 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,9 @@ "dependencies": { "express": "^4.17.1", "knex": "^0.95.11", - "sqlite3": "^5.0.2" + "sqlite3": "^5.0.2", + "vin-validator": "^1.0.0", + "yup": "^0.32.11" }, "repository": { "type": "git",