diff --git a/api/.DS_Store b/api/.DS_Store new file mode 100644 index 000000000..3db247eda Binary files /dev/null and b/api/.DS_Store differ diff --git a/api/hubs/hubs-model.js b/api/hubs/hubs-model.js new file mode 100644 index 000000000..5d0043797 --- /dev/null +++ b/api/hubs/hubs-model.js @@ -0,0 +1,53 @@ +const db = require('../../data/dbConfig.js'); + +module.exports = { + find, + findById, + add, + remove, + update, + findHubMessages +}; + +function find(query) { + let { page = 1, limit = 5, sortby = 'id', sortdir = 'asc' } = query; + const offset = limit * (page - 1); + + let rows = db('hubs') + .orderBy(sortby, sortdir) + .limit(limit) + .offset(offset); + + return rows; +} + +function findById(id) { + return db('hubs') + .where({ id }) + .first(); +} + +async function add(hub) { + const [id] = await db('hubs').insert(hub); + + return findById(id); +} + +function remove(id) { + return db('hubs') + .where({ id }) + .del(); +} + +function update(id, changes) { + return db('hubs') + .where({ id }) + .update(changes, '*'); +} + +function findHubMessages(hubId) { + return db('messages as m') + .join('hubs as h', 'm.hub_id', 'h.id') + .select('m.id', 'm.text', 'm.sender', 'h.id as hubId', 'h.name as hub') + .where({ hub_id: hubId }); +} diff --git a/api/hubs/hubs-router.js b/api/hubs/hubs-router.js new file mode 100644 index 000000000..8ec0fe734 --- /dev/null +++ b/api/hubs/hubs-router.js @@ -0,0 +1,117 @@ +const express = require('express'); + +const Hubs = require('./hubs-model.js'); +const Messages = require('../messages/messages-model.js'); + +const router = express.Router(); + +// middleware +const checkHubId = async (req, res, next) => { + const {id} = req.params; + + try { + const hub = await Hubs.findById(id); + + if (!hub) { + res.status(404).json({message: `no hub ${id}, error`}) + } else { + req.hub = hub; // take the request object and make a new property inside it + next(); + } + + } catch(e) { + res.status(500).json({message: e.message}) + } +} + +const checkMessage = (req, res, next) => { // middleware to check the payload + if (!req.body.sender || !req.body.text) { + res.status(400).json("message and sender required") + } else { + next(); + } +} + +router.get('/', (req, res, next) => { + Hubs.find(req.query) + .then(hubs => { + res.status(200).json(hubs); + }) + .catch(error => { + // log error to server + next(error) + }); +}); + +router.get('/:id', checkHubId, (req, res, next) => { + res.status(200).json(req.hub); // this is from the new property we just made in the middleware +}); + +router.post('/', (req, res) => { + Hubs.add(req.body) + .then(hub => { + res.status(201).json(hub); + }) + .catch(error => { + // log error to server + next(error) + }); +}); + +router.delete('/:id', checkHubId, (req, res, next) => { + Hubs.remove(req.params.id) + .then(() => { + res.status(200).json({ message: 'The hub has been nuked' }); + }) + .catch(error => { + // log error to server when we try to delete the hub, after we already checked the id + next(error) + }); +}); + +router.put('/:id', checkHubId, (req, res, next) => { + Hubs.update(req.params.id, req.body) + .then(hub => { + res.status(200).json(hub); + }) + .catch(error => { + // log error to server + next(error) + }); +}); + +router.get('/:id/messages', checkHubId, (req, res, next) => { + Hubs.findHubMessages(req.params.id) + .then(messages => { + res.status(200).json(messages); + }) + .catch(error => { + // log error to server + next(error) + }); +}); + +router.post('/:id/messages', checkHubId, checkMessage, (req, res, next) => { + const messageInfo = { ...req.body, hub_id: req.params.id }; + + Messages.add(messageInfo) + .then(message => { + res.status(210).json(message); + }) + .catch(error => { + // log error to server + next(error) + }); +}); + +// ERROR ENDPOINT +// ERROR ENDPOINT +// ERROR ENDPOINT +router.use((err, req, res, next) => { + res.status(500).json({ + message: 'Error somewhere in there my dude', + error: err.message + }); +}) + +module.exports = router; diff --git a/api/messages/messages-model.js b/api/messages/messages-model.js new file mode 100644 index 000000000..31afb02c1 --- /dev/null +++ b/api/messages/messages-model.js @@ -0,0 +1,45 @@ +const db = require('../../data/dbConfig.js'); + +module.exports = { + find, + findById, + add, + remove, + update +}; + +function find(query) { + let { page = 1, limit = 5, sortby = 'id', sortdir = 'asc' } = query; + const offset = limit * (page - 1); + + let rows = db('messages') + .orderBy(sortby, sortdir) + .limit(limit) + .offset(offset); + + return rows; +} + +function findById(id) { + return db('messages') + .where({ id }) + .first(); +} + +async function add(message) { + const [id] = await db('messages').insert(message); + + return findById(id); +} + +function remove(id) { + return db('messages') + .where({ id }) + .del(); +} + +function update(id, changes) { + return db('messages') + .where({ id }) + .update(changes, '*'); +} diff --git a/api/middlewares/middleware.js b/api/middlewares/middleware.js new file mode 100644 index 000000000..00ec567bb --- /dev/null +++ b/api/middlewares/middleware.js @@ -0,0 +1,30 @@ +// make your own middleware [ client --> Middleware (call next()) --> server ] / pay close attention to where and when your middleweare is being applied +const logQuote = (coin) => (req, res, next) => { + console.log('a penny saved is a penny earned'); + + if (coin == 'quarter') { + console.log('yeet'); + next(); + } else if (coin == 'penny' || 'nickle') { + console.log(`smol ${coin} bb`); + next(); + } else { + res.json("not a valid coin"); + } + +} + +const checkWord = (req, res, next) => { + if (req.query.word === "bad") { + res.json(`you can't proceed with the word: ${req.query.word}`); + } else { + next(); + } +} + + + +module.exports = { + logQuote, + checkWord +} \ No newline at end of file diff --git a/api/server.js b/api/server.js new file mode 100644 index 000000000..3e4e293d3 --- /dev/null +++ b/api/server.js @@ -0,0 +1,31 @@ +const express = require('express'); // importing a CommonJS module + +const hubsRouter = require('./hubs/hubs-router.js'); + +const morgan = require("morgan") +const helmet = require("helmet") + +const { logQuote, checkWord } = require('./middlewares/middleware') + +const server = express(); + +server.use(express.json()); +server.use(morgan("dev")) // global middleware, in the console, lets you know what end point, the status, and how long it took to do +server.use(helmet()) // global middlewear that protects header in requests +server.use(logQuote("penny")) // your own middleware + +server.use('/api/hubs', hubsRouter); + +server.get('/', checkWord, (req, res) => { + res.send(` +
Welcome to the Hubs API
+ `); +}); + +server.use('*', (req, res) => { + // catch all 404 errors middleware + res.status(404).json({ message: `${req.method} ${req.baseUrl} not found!` }); +}); + +module.exports = server; diff --git a/data/.DS_Store b/data/.DS_Store new file mode 100644 index 000000000..e267b16b1 Binary files /dev/null and b/data/.DS_Store differ diff --git a/data/dbConfig.js b/data/dbConfig.js new file mode 100644 index 000000000..f9a4ae63b --- /dev/null +++ b/data/dbConfig.js @@ -0,0 +1,5 @@ +const knex = require('knex'); + +const config = require('../knexfile.js'); + +module.exports = knex(config.development); diff --git a/data/hubs.db3 b/data/hubs.db3 new file mode 100644 index 000000000..5511563dc Binary files /dev/null and b/data/hubs.db3 differ diff --git a/data/migrations/20190211125952_bootstrap.js b/data/migrations/20190211125952_bootstrap.js new file mode 100644 index 000000000..4f29cf844 --- /dev/null +++ b/data/migrations/20190211125952_bootstrap.js @@ -0,0 +1,25 @@ +exports.up = function (knex) { + return knex.schema + .createTable('hubs', tbl => { + tbl.increments(); + tbl.string('name').notNullable().unique(); + }) + .createTable('messages', tbl => { + tbl.increments(); + tbl.string('sender').notNullable(); + tbl.text('text').notNullable(); + tbl.integer('hub_id') + .unsigned() + .notNullable() + .references('id') + .inTable('hubs') + .onDelete('CASCADE') + .onUpdate('CASCADE'); + }); +}; + +exports.down = function (knex) { + return knex.schema + .dropTableIfExists('messages') + .dropTableIfExists('hubs'); +}; diff --git a/data/seeds/000-cleanup.js b/data/seeds/000-cleanup.js new file mode 100644 index 000000000..825dbfd6d --- /dev/null +++ b/data/seeds/000-cleanup.js @@ -0,0 +1,8 @@ +const cleaner = require('knex-cleaner'); + +exports.seed = function(knex) { + return cleaner.clean(knex, { + mode: 'truncate', + ignoreTables: ['knex_migrations', 'knex_migrations_lock'], + }); +}; diff --git a/data/seeds/001-hubs.js b/data/seeds/001-hubs.js new file mode 100644 index 000000000..5ba7ec29b --- /dev/null +++ b/data/seeds/001-hubs.js @@ -0,0 +1,24 @@ +exports.seed = function(knex, Promise) { + return knex('hubs').insert([ + { name: 'api-1' }, // 1 + { name: 'api-2' }, // 2 + { name: 'api-3' }, // 3 + { name: 'api-4' }, // 4 + { name: 'db-1' }, // 5 + { name: 'db-2' }, // 6 + { name: 'db-3' }, // 7 + { name: 'db-4' }, // 8 + { name: 'auth-1' }, // 9 + { name: 'auth-2' }, // 10 + { name: 'auth-3' }, // 11 + { name: 'auth-4' }, // 12 + { name: 'testing-1' }, // 13 + { name: 'testing-2' }, // 14 + { name: 'testing-3' }, // 15 + { name: 'testing-4' }, // 16 + { name: 'build-1' }, // 17 + { name: 'build-2' }, // 18 + { name: 'build-3' }, // 19 + { name: 'build-4' }, // 20 + ]); +}; diff --git a/data/seeds/002-messages.js b/data/seeds/002-messages.js new file mode 100644 index 000000000..3b9491f23 --- /dev/null +++ b/data/seeds/002-messages.js @@ -0,0 +1,110 @@ +exports.seed = function (knex) { + return knex('messages').insert([ + { + sender: 'Frodo', + text: 'Go back Sam, I am goin gto Mordor alone', + hub_id: 1, + }, + { + sender: 'Sam', + text: 'Of course you are, and I am coming with you', + hub_id: 1, + }, + { + sender: 'Boromir', + text: 'One does nto simply walk into Mordor', + hub_id: 1, + }, + { + sender: 'Aragorn', + text: 'Deeds will not be less valiant because they are unpraised', + hub_id: 2, + }, + { + sender: 'Lady Galadriel', + text: 'Even the smallest person can change the course of history', + hub_id: 2, + }, + { + sender: 'Sam', + text: "There is some good in this world, Mr. Frodo... and it's worth figting for", + hub_id: 2, + }, + { + sender: 'Aragorn', + text: 'A hunted man sometimes wearies of distrust and longs for friendship', + hub_id: 2, + }, + { + sender: 'Lord Elrond', + text: 'Yet such is oft the course of deeds that move the wheels of the world: small hands do them because the must, while the eyes of the great are elsewhere', + hub_id: 2, + }, + { + sender: 'Boromir', + text: 'It is a strange fate that we should suffer so much fear and doubt over so small a thing... such a little thing', + hub_id: 2, + }, + { + sender: 'Gandalf', + text: 'He that breaks a thing to find out what it is, has left the path of wisdom', + hub_id: 2, + }, + { + sender: 'Gandalf', + text: 'All we have to decide is what to do with the time that is given us', + hub_id: 2, + }, + { + sender: 'Gandalf', + text: 'The burned hand teaches best. After that advice about fire goes to the heart', + hub_id: 2, + }, + { sender: 'Gimli', text: 'Never toss a dwarf!', hub_id: 2 }, + { + sender: 'Gimli', + text: 'Faithless is he that says farewell when the road darkens', + hub_id: 2, + }, + { + sender: 'Arwen', + text: 'Your time will come. You will face the same Evil, and you will defeat it', + hub_id: 3, + }, + { + sender: 'Gandalf', + text: 'A wizard is never late, Frodo Baggins. Nor is he early. He arrives precisely when he means to', + hub_id: 3, + }, + { + sender: 'Sam', + text: "It's the job that's never started as takes longest to finish", + hub_id: 3, + }, + { + sender: 'Frodo', + text: 'I will take the Ring, though I do not know the way', + hub_id: 3, + }, + { + sender: 'Lady Galadriel', + text: 'Seeing is both good and perilous', + hub_id: 3, + }, + { + sender: 'Aragorn', + text: 'It is but a shadow and a thought that you love. I cannot give you what you seek', + hub_id: 3, + }, + { + sender: 'Frodo', + text: "I don't know and I would rather not guess", + hub_id: 3, + }, + { + sender: 'Bilbo Baggins', + text: "It's a dangerous business, Frodo, going out your door. You step onto the road, and if you don't keep your feet, there's no knowing where you might be swept off to", + hub_id: 3, + }, + ]); +}; diff --git a/index.js b/index.js new file mode 100644 index 000000000..189be41a3 --- /dev/null +++ b/index.js @@ -0,0 +1,7 @@ +const server = require('./api/server.js'); + +const port = 9000; + +server.listen(port, () => { + console.log(`\n*** Server Running on http://localhost:${port} ***\n`); +}); diff --git a/knexfile.js b/knexfile.js new file mode 100644 index 000000000..f297a2bf1 --- /dev/null +++ b/knexfile.js @@ -0,0 +1,21 @@ +module.exports = { + development: { + client: 'sqlite3', + connection: { + filename: './data/hubs.db3', + }, + useNullAsDefault: true, + pool: { + afterCreate: (conn, done) => { + // runs after a connection is made to the sqlite engine + conn.run('PRAGMA foreign_keys = ON', done); + }, + }, + migrations: { + directory: './data/migrations', + }, + seeds: { + directory: './data/seeds', + }, + }, +};