Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
1 change: 1 addition & 0 deletions data/hike/1a1ee70e-e7ba-45d7-b74b-b4b0233be0cb.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"id":"1a1ee70e-e7ba-45d7-b74b-b4b0233be0cb","name":"party hike","location":"party town"}
1 change: 1 addition & 0 deletions data/hike/2ec48c31-1e52-4701-9857-87cd2bdc25f0.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"id":"2ec48c31-1e52-4701-9857-87cd2bdc25f0","name":"party hike","location":"party town"}
1 change: 1 addition & 0 deletions data/hike/e56672f3-362a-4a2b-b4b2-c683f1727eb5.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"id":"e56672f3-362a-4a2b-b4b2-c683f1727eb5","name":"party hike","location":"party town"}
7 changes: 7 additions & 0 deletions lib/cors-middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use strict';

module.exports = function(req,res,next){
res.append('Access-Control-Allow-Origin', '*');
res.append('Access-Control-Allow-Headers', '*');
next();
};
21 changes: 21 additions & 0 deletions lib/error-middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
'use strict';

const createError = require('http-errors');
const debug = require('debug')('hike:error-middlware');

module.exports = function(err, req, res, next ){
console.error(err.message);

if (err.status){
debug('user error');

res.status(err.status).send(err.name);
next();
return;
}

debug('server errror');
err = createError(500, err.message);
res.status(err.status).send(err.name);
next();
};
57 changes: 57 additions & 0 deletions lib/storage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
'use strict';

const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'), {suffix: 'Prom'});
const createError = require('http-errors');
const debug = require('debug')('hike:storage');

module.exports = exports = {};

exports.createItem = function(schemaName, item){
debug('createItem');

if (!schemaName) return Promise.reject(createError(400, 'expected schema name'));
if (!item) return Promise.reject(createError(400, 'expected item'));

let json = JSON.stringify(item);
return fs.writeFileProm(`${__dirname}/../data/${schemaName}/${item.id}.json`, json)
.then( () => item)
.catch( err => Promise.reject(createError(500, err.message)));
};

exports.fetchItem = function(schemaName, id){
debug('fetchItem');

if (!schemaName) return Promise.reject(createError(400, 'expected schema name'));
if (!id) return Promise.reject(createError(400, 'expected id'));

return fs.readFileProm(`${__dirname}/../data/${schemaName}/${id}.json`)
.then(data => {
try {
let item = JSON.parse(data.toString());
return item;
} catch (err) {
return Promise.reject(createError(500, err.message));
}
})
.catch(err => Promise.reject(createError(404, err.message)));
};


exports.deleteItem = function(schemaName, id) {
debug('deleteItem');

if (!schemaName) return Promise.reject(createError(400, 'expected schema name'));
if (!id) return Promise.reject(createError(400, 'expected id'));

return fs.unlinkProm(`${__dirname}/../data/${schemaName}/${id}.json`)
.catch( err => Promise.reject(createError(404, err.message)));
};


exports.availIDs = function(schemaName) {
debug('available ids');
return fs.readdirProm(`${__dirname}/../data/${schemaName}`)
.then( files => files.map(name => name.split('.json')[0]))
.catch( err => Promise.reject(createError(404, err.message)));
};
57 changes: 57 additions & 0 deletions model/hike.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
'use strict';

const uuidv4 = require('uuid/v4');
const createError = require('http-errors');
const debug = require('debug')('hike:hike');
const storage = require('../lib/storage.js');

const Hike = module.exports = function(name, location) {
debug('hike constructr');

if(!name) throw new Error('expected name');
if(!location) throw new Error('expected content');

this.id = uuidv4();
this.name = name;
this.location = location;
};

Hike.createHike = function(_hike) {
debug('createHike');

try {
let hike = new Hike(_hike.name, _hike.location);
return storage.createItem('hike', hike);
} catch (err) {
return Promise.reject(err);
}
};

Hike.fetchHike = function(id) {
debug('fetch Hike ');
return storage.fetchItem('hike', id);
};

Hike.updateHike = function(id, _hike) {
debug('update hike');

return storage.fetchItem('hike', id)
.catch( err=> Promise.reject(createError(404, err.message)))
.then (hike => {
for (var prop in hike) {
if(prop === 'id')continue;
if (_hike[prop]) hike[prop] = _hike[prop];
}
return storage.createItem('hike', hike);
});
};

Hike.deleteHike = function(id){
debug('deleteHike');
return storage.deleteItem('hike', id);
};

Hike.fetchIDs = function() {
debug('fetchIDs');
return storage.availIDs('hike');
};
39 changes: 39 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"name": "12-express-middleware",
"version": "1.0.0",
"description": "![cf](https://i.imgur.com/7v5ASc8.png) 12: Express Middleware ======",
"main": "server.js",
"directories": {
"test": "test"
},
"scripts": {
"test": "DEBUG='hike*' ./node_modules/mocha/bin/mocha",
"start": "DEBUG='hike*' node server.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/andyfiveeleven/12-express-middleware.git"
},
"keywords": [],
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/andyfiveeleven/12-express-middleware/issues"
},
"homepage": "https://github.com/andyfiveeleven/12-express-middleware#readme",
"devDependencies": {
"chai": "^4.1.0",
"mocha": "^3.5.0",
"superagent": "^3.5.2"
},
"dependencies": {
"bluebird": "^3.5.0",
"body-parser": "^1.17.2",
"debug": "^2.6.8",
"express": "^4.15.3",
"http-errors": "^1.6.1",
"mkdirp": "^0.5.1",
"morgan": "^1.8.2",
"uuid": "^3.1.0"
}
}
41 changes: 41 additions & 0 deletions route/hike-router.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
'use strict'

const Router = require('express').Router;
const jsonParser = require('body-parser').json();
const debug = require('debug')('hike:hike-route');
const Hike = require('../model/hike.js');
const hikeRouter = new Router();

hikeRouter.post('/api/hike', jsonParser, function(req,res,next){
debug('POST: /api/hike');

Hike.createHike(req.body)
.then( hike => res.json(hike))
.catch(err => next(err));
});

hikeRouter.get('/api/hike/:id', function(req,res,next ){
debug('GET: /api/hike:id');

Hike.fetchHike(req.params.id)
.then(hike=> res.json(hike))
.catch( err=> next(err));
});

hikeRouter.get('/api/hike', function(req, res, next){
debug('GET /api/hike');

Hike.fetchIDs()
.then( ids => res.json(ids))
.catch( err => next(err));
});

hikeRouter.put('/api/hike', jsonParser, function(req, res, next) {
debug('PUT: /api/hike');

Hike.updateHike(req.query.id,req.body)
.then( hike => res.json(hike))
.catch( err => next(err));
});

module.exports = hikeRouter;
22 changes: 22 additions & 0 deletions server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use strict'

const morgan = require('morgan');
const express = require('express');
const createError = require('http-errors');
const debug = require('debug')('hike:server');

const hikeRouter = require('./route/hike-router.js');
const cors = require('./lib/cors-middleware.js');
const errors = require('./lib/error-middleware.js');

const PORT = process.env.PORT || 3000;
const app = express();

app.use(morgan('dev'));
app.use(cors);
app.use(hikeRouter);
app.use(errors);

app.listen(PORT, () => {
console.log(`server up on ${PORT}`);
});
117 changes: 117 additions & 0 deletions test/hike-route-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
'use strict';

const expect = require('chai').expect;
const request = require('superagent');
const Hike = require('../model/hike.js');
const url = 'http://localhost:8000';

require('../server.js');

const exampleHike = {
name: 'party hike',
location: 'party town'
};

describe('hike routes', function() {
describe('GET: /api/hike', function(){
describe('with a valid id', function(){
before( done => {
Hike.createHike(exampleHike)
.then( hike => {
this.tempHike = hike;
done();
})
.catch(err => done(err));
});

after(done => {
Hike.deleteHike(this.tempHike.id)
.then( () => done())
.catch( err => done(err));
});

it('should return a hike', done => {
request.get(`${url}/api/hike/${this.tempHike.id}`)
.end((err, res) => {
if(err) return done(err);
expect(res.status).to.equal(200);
expect(res.body.id).to.equal(this.tempHike.id);
expect(res.body.name).to.equal(this.tempHike.name);
expect(res.body.location).to.equal(this.tempHike.location);
done();
});
});

describe('with an invalid id', function() {
it('shoulr respond witha 404', done => {
request.get(`${url}/api/hike/1234565678`)
.end((err, res)=> {
expect(res.status).to.equal(404);
done();
});
});
});
});
});

describe('POST: /api/hike', function(){
describe('with a valid body', function(){
after(done => {
if(this.tempHike) {
Hike.deleteHike(this.tempHike.id)
.then( () => done())
.catch( err=> done(err));
}
});

it('should return a hike', done =>{
request.post(`${url}/api/hike`)
.send(exampleHike)
.end((err, res) => {
if (err) return done(err);
expect(res.status).to.equal(200);
expect(res.body.name).to.equal(exampleHike.name);
expect(res.body.location).to.equal(exampleHike.location);
this.tempHike = res.body;
done();
});
});
});
});

describe('PUT /api/hike', function(){
describe('with a valid id and body', function(){
before( done => {
Hike.createHike(exampleHike)
.then( hike => {
this.tempHike = hike;
done();
})
.catch (err => done(err));
});

after(done => {
if (this.tempHike) {
Hike.deleteHike(this.tempHike.id)
.then( ()=> done())
.catch(done);
}
});

it('should return a hike', done =>{
let updateHike = {name: 'party hike', location: 'party town'};
request.put(`${url}/api/hike?id=${this.tempHike.id}`)
.send(updateHike)
.end((err, res)=> {
if (err)return done(err);
expect(res.status).to.equal(200);
expect(res.body.id).to.equal(this.tempHike.id);
for (var prop in updateHike){
expect(res.body[prop]).to.equal(updateHike[prop])
}
done();
});
});
});
});
});