Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
d568d2b
initial commit, include basic files
maschigokae Dec 22, 2016
4f3b9c3
add dependencies, start and test scripts
maschigokae Dec 22, 2016
921d077
add require statements for dependencies, add debug to listener
maschigokae Dec 22, 2016
e1c5f7f
add references to mongoose
maschigokae Jan 2, 2017
58972a9
add a BEV data modeling module
maschigokae Jan 3, 2017
4386651
build out BEV data mongoose schema
maschigokae Jan 3, 2017
21d5b8e
move BEV schema file to model directory
maschigokae Jan 3, 2017
3a541dc
add http-errors to dependencies
maschigokae Jan 4, 2017
125091a
set up BEV router
maschigokae Jan 4, 2017
b74582d
finish building out BEV routes
maschigokae Jan 4, 2017
09d400e
add error middleware
maschigokae Jan 4, 2017
f2f7910
set up BEV routes test file
maschigokae Jan 4, 2017
7ec7e9a
update app dependencies with new routes, middleware
maschigokae Jan 4, 2017
eeef7c1
build out POST test, create todos for other route tests
maschigokae Jan 4, 2017
cb308a4
build out GET route test for BEV data
maschigokae Jan 5, 2017
69b2aee
build out PUT route test for BEV data
maschigokae Jan 5, 2017
0ab9ae9
build out DELETE route test for BEV data
maschigokae Jan 5, 2017
697abc9
correct formulation of lastupdated property
maschigokae Jan 5, 2017
fa3fec7
create fetch-all-ids GET route
maschigokae Jan 5, 2017
a01c9d2
add TODO for GET request for all ids in the database
maschigokae Jan 5, 2017
1d8f482
formatting cleanup
maschigokae Jan 5, 2017
60a9108
add error handling for PUT request where no request body was provided
maschigokae Jan 6, 2017
d555c50
write tests for BEV routes with invalid or missing-resource requests
maschigokae Jan 6, 2017
092a452
Merge pull request #1 from maschigokae/single-resource-foundation
maschigokae Jan 6, 2017
f381ff8
add lab requirements md file for two resource expansion
maschigokae Jan 6, 2017
e24b680
build out model for review data
maschigokae Jan 6, 2017
98bc7d9
add integer validator to rating property
maschigokae Jan 6, 2017
3257c44
correct review model property name
maschigokae Jan 6, 2017
b286794
build out functionality to add reviews to BEV objects
maschigokae Jan 6, 2017
2fc9107
build out review router for POST method
maschigokae Jan 6, 2017
30e7356
add requirements for review routes
maschigokae Jan 6, 2017
1c827aa
correct wrong require statement
maschigokae Jan 6, 2017
d615608
correct wrong app name
maschigokae Jan 6, 2017
e4b45bd
build out, verify initial POST test for review routes
maschigokae Jan 6, 2017
a61c080
add populate reviews call in GET request
maschigokae Jan 7, 2017
dec5ef7
add review GET route
maschigokae Jan 7, 2017
3e7979c
add tests for invalid POST request and valid GET request
maschigokae Jan 7, 2017
8f35193
modify invalid id error handling
maschigokae Jan 7, 2017
eec8b70
add test for invalid GET request
maschigokae Jan 7, 2017
2e3ea08
build out PUT and DELETE routes
maschigokae Jan 7, 2017
c2069b2
write, verify tests for PUT and DELETE routes
maschigokae Jan 7, 2017
ebc5fb3
add error handling for invalid, but correctly formatted ID strings
maschigokae Jan 7, 2017
f7af914
add gulp tasks
maschigokae Jan 7, 2017
a25f828
refactor BEV route tests
maschigokae Jan 7, 2017
7e7d868
build out README documentation
maschigokae Jan 7, 2017
abfcf97
edit README documentation for clarity
maschigokae Jan 7, 2017
ea6cda3
add clarification re posting and looking up reviews
maschigokae Jan 7, 2017
88163cd
add documentation for DELETE requests
maschigokae Jan 7, 2017
7910148
Merge pull request #2 from maschigokae/add-second-resource
maschigokae Jan 7, 2017
81c43ff
correct typo in documentation
maschigokae Jan 7, 2017
0bd4030
added error handling for empty array of BEV ids
maschigokae Jan 12, 2017
75c1a70
add expect error block
maschigokae Jan 12, 2017
db9a089
add some missing semi-colons
maschigokae Jan 12, 2017
8e11448
Merge pull request #3 from maschigokae/refactor-get-all-method
maschigokae Jan 12, 2017
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"
}
105 changes: 105 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# 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*
24 changes: 24 additions & 0 deletions 14-mongo_express_two_resource.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
![CF](https://camo.githubusercontent.com/70edab54bba80edb7493cad3135e9606781cbb6b/687474703a2f2f692e696d6775722e636f6d2f377635415363382e706e67) Lab 13 & 14 - Mongo & Express Two Resource API
===

## To Submit this Assignment
* Fork this repository
* Write all of your code in a directory named `lab-` + `<your name>` **e.g.** `lab-brian`
* Push to your repository
* Submit a pull request to this repository
* Submit a link to your PR in canvas
* Write a question and observation on canvas

## Include
* `package.json`
* `.eslintrc`
* `gulpfile.js`
* `.gitignore`
* `README.md`

## Description
* Continue working on the `express` and `mongoDB` REST API that you started yesterday
* Include an additional resource that contains a "relationship" to the single resource that has already been created
* Create `GET`, `POST`, `PUT`, and `DELETE` routes for your newly added resource
* Test your application to ensure that it meets the standard criteria of a working **full CRUD** REST API
* Use `populate` in the `get()` route logic your `/api/new-resource-name/:id` route to populate the associated property used to house related resources **(ex: `List.findById(req.params.id).populate('notes')`)**
51 changes: 51 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
## 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.

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.

The API saves data to a MongoDB database and returns entries for individual vehicles as JSON objects.

There are currently two resources for this API:
* Vehicles: general info about individual vehicles
* Reviews: reviews and ratings for vehicles submitted by users

Each vehicle object has 5 user-editable properties:
* vehicle -- Make and Model of Vehicle. Example: "Nissan Leaf". **Must be a string.**
* info -- Details about vehicle. Example: "practical and affordable hatchback". **Must be a string.**
* msrp -- Vehicle's manufacturer-suggested retail price (MSRP) for typically equipped vehicle, in USD. **Must be a number.**
* range -- Vehicle's EPA-rated range on a full charge, in miles. **Must be a number.**
* mpge -- Vehicle's EPA-rated "miles-per-gallon equivalent" (MPGe). **Must be a number.**

Each review object has 4 user-editable properties:
* title -- Title or headline of review. **Must be a string.**
* authorName -- Reviewer's name or handle. **Must be a string.**
* reviewText -- Main body text of the review. **Must be a string.**
* rating -- A number from 1 to 10, with 1 meaning "horrible" and 10 meaning "excellent". **Must be a whole number no less than 1 and no greater than 10.**

Additionally, each individual vehicle and review object has an "id" property, which is generated by MongoDB and not user-editable. The id property holds a randomly generated string composed of letters, numbers, and dashes. The id is used as a URI parameter in each http request against an existing individual vehicle and/or review object.

## Summary

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 such as market availability, quick-charging options and standards, and more technical specs will eventually be added (as additional API properties).


## How to use this API

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 MongoDB installed locally. You will also 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, install the necessary dependencies for running the app by typing `npm i`
* In a **separate** window or pane of your command line interface, start MongoDB by typing `mongod`
* Then, start the node server by typing `npm run start`
* Let's add a sample vehicle to the API. In a **separate** window or pane of your command line interface (the first two are running the node server and MongoDB in the background), type `http POST localhost:3000/api/bev vehicle="Nissan Leaf" info="practical, affordable commuter and family vehicle" range=107 mpge=110`
* Let's add a second sample vehicle. Type `http POST localhost:3000/api/bev vehicle="Tesla Model X" info="classy, sporty, high-tech luxury SUV" msrp=100000 range=300 mpge=95`
* 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/` (make sure the closing forward-slash is present) and paste the id before submitting the command. You should see an object printed to the command line with the vehicle info.
* Let's post a review for the Tesla Model X. Copy the id for the vehicle you want to review. Type `http POST localhost:3000/api/bev/`, paste the vehicle id, then type `/review title="Amazing Car!" authorName="YourName" reviewText="This car will blow your mind!" rating=10`
* Now, when you look up the vehicle again, you will see this review and all other reviews associated with it, contained within an array in the "reviews" property. Alternatively, to look up a single review, type `http localhost:3000/api/bev/` paste the vehicle id, then type `/review/`, then paste the review id.
* You may update vehicle or review objects by typing `http PUT`, entering the URI for the vehicle and review (formatted the same as a GET request) and entering the updated information (formatted the same as a POST request). Unlike POST requests, updating all fields is not required. Say you decided you wanted to downgrade your rating and add a comment to the review body. You would type the request in the following format: `http PUT localhost:3000/api/bev/***vehicle-id-here***/review/***review-id-here*** reviewText="This car will blow your mind! The gullwing doors are a bit annoying though." rating=9` Now the rating and review text are updated, while the authorName and title fields remain unchanged.
* To delete a vehicle or review, type `http DELETE` followed by the full URI of the vehicle or review you wish to delete. Example request for deleting a review: `http DELETE localhost:3000/api/bev/***vehicle-id-here***/review/***review-id-here***`

Thanks for using my API. Check back soon for updates and improvements!
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']);
28 changes: 28 additions & 0 deletions lib/error-middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
'use strict'

const createError = require('http-errors');
const debug = require('debug')('bev:error-middleware');

module.exports = function(err, req, res, next) {
debug('error-middleware');

console.error('msg:', err.message);
console.error('name:', err.name);

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

if (err.name === 'ValidationError') {
err = createError(400, err.message);
res.status(err.status).send(err.name);
next();
return;
}

err. createError(500, err.message);
res.status(err.message).send(err.name);
next();
};
40 changes: 40 additions & 0 deletions model/bev.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
'use strict';

const mongoose = require('mongoose');
const createError = require('http-errors');
const debug = require('debug')('bev:bev');
const Schema = mongoose.Schema;

const Review = require('./review.js');

const bevSchema = Schema({
vehicle: { type: String, required: true },
info: { type: String, required: true },
msrp: { type: Number, required: true },
range: { type: Number, required: true },
mpge: { type: Number, required: true },
lastupdated: { type: Date, default: Date.now },
reviews: [{ type: Schema.Types.ObjectId, ref: 'review' }]
});

const BEV = module.exports = mongoose.model('bev', bevSchema);

BEV.findByIdAndAddReview = function(id, review) {
debug('find by ID and add review');

return BEV.findById(id)
.catch( err => Promise.reject(createError(404, err.message)))
.then( vehicle => {
review.vehicleID = vehicle._id;
this.tempVehicle = vehicle;
return new Review(review).save();
})
.then( review => {
this.tempVehicle.reviews.push(review._id);
this.tempReview = review;
return this.tempVehicle.save();
})
.then( () => {
return this.tempReview;
});
};
24 changes: 24 additions & 0 deletions model/review.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
'use strict';

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const reviewSchema = Schema ({
title: { type: String, required: true },
authorName: { type: String, required: true },
reviewText: { type: String, required: true },
rating: {
type: Number,
min: 1,
max: 10,
required: true,
validate: {
validator: Number.isInteger,
message: 'rating must be a whole number between 1 and 10.'
}
},
createdOn: { type: Date, default: Date.now },
vehicleID: { type: Schema.Types.ObjectId, required: true }
});

module.exports = mongoose.model('review', reviewSchema);
39 changes: 39 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"name": "13-mongo_and_express_api",
"version": "1.0.0",
"description": "Content coming soon.",
"main": "gulpfile.js",
"scripts": {
"test": "DEBUG='bev*' mocha",
"start": "DEBUG='bev*' node server.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/maschigokae/13-mongo_and_express_api.git"
},
"keywords": [],
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/maschigokae/13-mongo_and_express_api/issues"
},
"homepage": "https://github.com/maschigokae/13-mongo_and_express_api#readme",
"dependencies": {
"bluebird": "^3.4.7",
"body-parser": "^1.15.2",
"cors": "^2.8.1",
"debug": "^2.5.1",
"express": "^4.14.0",
"http-errors": "^1.5.1",
"mongoose": "^4.7.4",
"morgan": "^1.7.0"
},
"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"
}
}
Loading