Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
3f601c0
first commit, project in same state as end of lab 09
maschigokae Dec 20, 2016
ba5e0fe
add range property to BEV constructor
maschigokae Dec 20, 2016
64f62e2
refactor server to run on express
maschigokae Dec 20, 2016
348c336
add dependencies related to express refactor
maschigokae Dec 20, 2016
c92c314
bugfix - fix typo causing server error
maschigokae Dec 20, 2016
6535fb9
remove modules to be abstracted out with express
maschigokae Dec 20, 2016
05556f2
remove route module to be abstracted out with express
maschigokae Dec 20, 2016
5c67a6b
bugfix - add missing chained .json method on body-parser require expr…
maschigokae Dec 20, 2016
a05fb7d
set up morgan useage in server module
maschigokae Dec 20, 2016
41b888d
refactor for express, debug issues with new range property
maschigokae Dec 20, 2016
65f27e2
add start script
maschigokae Dec 20, 2016
d87f66f
add POST method, test to make sure it works
maschigokae Dec 20, 2016
f8d9fd9
update package.json with current lab repo info
maschigokae Dec 20, 2016
f841b52
update gitignore file to ignore a testing file and data directory
maschigokae Dec 21, 2016
d77195a
build out fetching methods for single entry and all entries
maschigokae Dec 21, 2016
b68e4af
build out GET routes for retrieving single entries or all entries
maschigokae Dec 21, 2016
b92aed9
set up npm test script in package.json
maschigokae Dec 21, 2016
1b52fd8
add http-errors, fix promise statements
maschigokae Dec 21, 2016
7fee526
add custom error status to errors stack, add delete method
maschigokae Dec 21, 2016
5d17362
add delete route to route stack in server file
maschigokae Dec 21, 2016
5494941
refactor tests
maschigokae Dec 21, 2016
350dd70
add debug statements to storage methods
maschigokae Dec 21, 2016
cda01c9
add require statement for debug module
maschigokae Dec 21, 2016
57c4d92
minor test edit, pass empty object into send call
maschigokae Dec 21, 2016
4a034a3
un-gitignore data so TAs have data directories and sample json data file
maschigokae Dec 21, 2016
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
21 changes: 21 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"rules": {
"no-console": "off",
"indent": [ "error", 2 ],
"quotes": [ "error", "single" ],
"semi": ["error", "always"],
"linebreak-style": [ "error", "unix" ]
},
"env": {
"es6": true,
"node": true,
"mocha": true,
"jasmine": true
},
"ecmaFeatures": {
"modules": true,
"experimentalObjectRestSpread": true,
"impliedStrict": true
},
"extends": "eslint:recommended"
}
107 changes: 107 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# Created by https://www.gitignore.io/api/macos,node,vim,linux

### macOS ###
*.DS_Store
.AppleDouble
.LSOverride

# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk


### Node ###
node_modules
# Logs
logs
*.log
npm-debug.log*

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules
jspm_packages

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity



### Vim ###
# swap
[._]*.s[a-w][a-z]
[._]s[a-w][a-z]
# session
Session.vim
# temporary
.netrwhist
*~
# auto-generated tag files
tags


### Linux ###

# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*

# KDE directory preferences
.directory

# Linux trash folder which might appear on any partition or disk
.Trash-*

# .nfs files are created when an open file is removed but is still being accessed
.nfs*

sample-req.js
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
## Hello and welcome to my Electric Vehicle API.

In the last decade, Electric vehicles have exploded in popularity with the advent of new battery technologies and practical vehicles hitting the market, from flashy luxury models like Tesla's Model S, to affordable commuter/family models like Nissan's Leaf. As technology improves and better, more affordable models hit the market, Electric vehicles have the potential to make a serious dent in pollution from automotive transportation. This is especially true in markets with relatively green power grids.

The purpose of this API is ultimately to be a resource for consumers interested in Electric Vehicles and green transportation in general, containing the most up-to-date information on Electric Vehicles. Info like range on a battery charge, MPGe, market availability, quick-charging options and standards will eventually be added as separate categories in terms of consumer info (object properties in terms of the API)

For this API, we define "Electric Vehicles" more technically and specifically as "Battery-Electric Vehicles," or BEVs. Only vehicles that are powered exclusively by electricity from a battery are included. This designation excludes plug-in-hybrid vehicles (like the Chevrolet Volt) and hydrogen fuel cell vehicles.

## How to use this API

Currently, this API is configured for use in the command line. It is set up to run on port 3000 of your computer's local IP (this IP can be accessed with the identifier `localhost`).

You will need a command line http tool installed. I recommend httpie, and I assume you have it installed for this example.

* In the command line, making sure you're in the root directory of your local version of the API, start the node server by typing `node server.js`
* Let's add a sample vehicle to the API. In a **separate** window or pane of your command line interface (your first window is running the node server), making sure you're still in the root directory of your local version of the API, type `http POST localhost:3000/api/bev vehicle="Nissan Leaf" info="108 mile-range on a single charge"`
* Let's add a second sample vehicle. Type `http POST localhost:3000/api/bev vehicle="Tesla Model S" info="300 mile-range on a single charge"`
* Let's get the unique id for each vehicle. Each id is a random string of numbers, letter, and dashes autogenerated with node-uuid. type `http localhost:3000/api/bev` This should print an array of ids (for this example there should be two) in the command line.
* Let's take one of the ids and look up the vehicle information it references. Copy the text of one of the ids. Then in the command line, type `http localhost:3000/api/bev?id=` and paste the id before submitting the command. You should see an object printed to the command line with the vehicle info.

Thanks for using my API. Check back soon for updates and improvements!
1 change: 1 addition & 0 deletions data/bev/507c19c0-c745-11e6-b109-8b592aaa39eb.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"id":"507c19c0-c745-11e6-b109-8b592aaa39eb","vehicle":"Nissan Leaf","info":"practical and affordable commuter and family hatchback","range":"120"}
23 changes: 23 additions & 0 deletions gulpfile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
'use strict';

const gulp = require('gulp');
const eslint = require('gulp-eslint');
const mocha = require('gulp-mocha');

gulp.task('test', function() {
gulp.src('./test/*-test.js', { read: false })
.pipe(mocha({ reporter: 'spec' }));
});

gulp.task('lint', function() {
gulp.src(['**/*.js', '!node_modules'])
.pipe(eslint())
.pipe(eslint.format())
.pipe(eslint.failAfterError());
});

gulp.task('dev', function() {
gulp.watch(['**/*.js', '!node_modules'], ['lint', 'test']);
});

gulp.task('default', ['dev']);
71 changes: 71 additions & 0 deletions lib/storage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
'use strict';

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

module.exports = exports = {};

exports.createEntry = function(schemaName, entry) {
debug('createEntry');

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

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

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

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 entry = JSON.parse(data.toString());
return entry;
} catch (err) {
return Promise.reject(createError(500, err.message));
};
})
.catch( err => Promise.reject(createError(404, err.message)));
};

exports.fetchAll = function(schemaName) {
debug('fetchAll');

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

// return array of saved filenames whose names (minus the '.json' filename extention) match vehicle IDs
return fs.readdirProm(`${__dirname}/../data/${schemaName}/`)
.then( fileNames => {
try {
var ids = [];
fileNames.forEach( fileName => {
// remove '.json' filename extention from array of IDs, push to new array to print to users' CLI
ids.push(fileName.replace('.json', ''));
});
return ids;

} catch (err) {
return Promise.reject(err);
};
})
.catch( err => Promise.reject(err));
};

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

if (!schemaName) return Promise.reject(createError(400, 'expected schemaName'));
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)));
};
52 changes: 52 additions & 0 deletions model/bevs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
'use strict';

// Model for data on Battery-Electric Vehicles (BEVs)

const uuid = require('node-uuid');
const createError = require('http-errors');
const debug = require('debug')('bev:bev');
const storage = require('../lib/storage.js');

const BEV = module.exports = function(vehicle, info, range) {
debug('BEV constructor');

if (!vehicle) throw createError(400, 'expected vehicle model name');
if (!info) throw createError(400, 'expected vehicle model info');
if (!range) throw createError(400, 'expected vehicle model range');
if (isNaN(range)) throw createError(400, 'expected range to be a number');

this.id = uuid.v1();
// vehicle make, model
this.vehicle = vehicle;
// general vehicle description
this.info = info;
// EPA-tested range on a single battery charge, in miles
// range must be a number
this.range = range;
};

BEV.createVehicle = function(_vehicle) {
debug('createVehicle');

try {
let bev = new BEV(_vehicle.vehicle, _vehicle.info, _vehicle.range);
return storage.createEntry('bev', bev);
} catch (err) {
return Promise.reject(err);
};
};

BEV.fetchVehicle = function(id) {
debug('fetchVehicle');
return storage.fetchEntry('bev', id);
};

BEV.fetchAllVehicles = function() {
debug('fetchAllVehicles');
return storage.fetchAll('bev');
};

BEV.deleteVehicle = function(id) {
debug('deleteVehicle');
return storage.deleteEntry('bev', id);
};
38 changes: 38 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"name": "11-express_single_resource_api",
"version": "1.0.0",
"description": "",
"main": "server.js",
"scripts": {
"test": "DEBUG='bev*' mocha",
"start": "DEBUG='bev*' node server.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/maschigokae/11-express_single_resource_api.git"
},
"keywords": [],
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/maschigokae/11-express_single_resource_api/issues"
},
"homepage": "https://github.com/maschigokae/11-express_single_resource_api#readme",
"dependencies": {
"bluebird": "^3.4.6",
"body-parser": "^1.15.2",
"debug": "^2.4.5",
"express": "^4.14.0",
"http-errors": "^1.5.1",
"morgan": "^1.7.0",
"node-uuid": "^1.4.7"
},
"devDependencies": {
"chai": "^3.5.0",
"gulp": "^3.9.1",
"gulp-eslint": "^3.0.1",
"gulp-mocha": "^3.0.1",
"mocha": "^3.2.0",
"superagent": "^3.3.1"
}
}
48 changes: 48 additions & 0 deletions server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
'use strict';

const express = require('express');
const morgan = require('morgan');
const createError = require('http-errors');
const jsonParser = require('body-parser').json();
const debug = require('debug')('bev:server');

const app = express();
const BEV = require('./model/bevs.js');

const PORT = 3000;

app.use(morgan('dev'));

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

BEV.createVehicle(req.body)
.then( vehicle => res.json(vehicle))
.catch( err => next(err));
});

app.get('/api/bev', function(req, res, next) {
debug('GET: api/bev');

if (req.query.id) {
BEV.fetchVehicle(req.query.id)
.then( vehicle => res.json(vehicle))
.catch( err => next(err));
};

if (!req.query.id) {
BEV.fetchAllVehicles()
.then( vehicles => res.json(vehicles))
.catch( err => next(err));
};
});

app.delete('/api/bev', function(req, res, next) {
BEV.deleteVehicle(req.query.id)
.then( vehicle => res.json(vehicle))
.catch( err => next(err));
});

app.listen(PORT, () => {
console.log(`SERVER RUNNING ON PORT ${PORT}`);
});
Loading