Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lab-Yana #4

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
21 changes: 21 additions & 0 deletions lab-yana/.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"
}
117 changes: 117 additions & 0 deletions lab-yana/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# Created by https://www.gitignore.io/api/node,vim,osx,macos,linux

*node_modules

### Node ###
# 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-v][a-z]
[._]*.sw[a-p]
[._]s[a-v][a-z]
[._]sw[a-p]
# session
Session.vim
# temporary
.netrwhist
*~
# auto-generated tag files
tags


### OSX ###
*.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


### macOS ###
# Icon must end with two \r
# Thumbnails
# Files that might appear in the root of a volume
# Directories potentially created on remote AFP share


### 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*

# End of https://www.gitignore.io/api/node,vim,osx,macos,linux
5 changes: 5 additions & 0 deletions lab-yana/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
### Two Resource RESTful full-CRUD API

A combination of art and fine tuned precision brings you an amazing API to keep track of your mountain peaks. Both resources are full-CRUD and each is well tested (19 tests total for GET, PUT, DELETE, and POST routes). So well tested, in fact, that it takes 2.63 seconds for all the tests to finish running!

##### created by _Yana the Code Breaker_
20 changes: 20 additions & 0 deletions lab-yana/gulpfile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
'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() {
return 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']);
34 changes: 34 additions & 0 deletions lab-yana/lib/error-middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
'use strict';

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

module.exports = function(err, req, res, next) {
debug('error-middleware');
console.error(`msg: ${err.message}`);
console.error(`name: ${err.name}`);


if (err.name === 'ValidationError') { //if the error is a mongoose ValidationError, create a code 400 since it is due to an invalid/noneexistent req.body
err = createError(400, err.message);
res.status(err.status).send(err.message);
next();
return;
}

if(err.name === 'CastError') { //if the error is a mongoose CastError, create a 404 since that means the id was not found
err = createError(404, err.message);
res.status(err.status).send(err.message);
next();
return; //exit function so you don't hit the internal server error code below
}

if (err.status) { //if the error came with a status code, do this
res.status(err.status).send(err.message);
next();
return;
}

err = createError(500, err.message); //if we don't hit any of the above, send a 500 internal server error to signify that we don't know what the fuck is wrong
res.status(err.status).send(err.message);
};
32 changes: 32 additions & 0 deletions lab-yana/model/mountains.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
'use strict';

const debug = require('debug')('peak:mountains');
const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const Peak = require('./peak.js');

const mountainsSchema = Schema({
name: { type: String, required: true },
timestamp: { type: Date, required: true },
peaks: [{ type: Schema.Types.ObjectId, ref: 'peak' }]
});

const Mountains = module.exports = mongoose.model('mountains', mountainsSchema);

Mountains.findByIdAndAddPeak = function(id, peak, next) { //static method that takes in a peak and its ID and puts it in the peak Mountains property
debug('findByIdAndAddPeak');
return Mountains.findById(id)
.then(mountains => {
peak.mountainsID = mountains._id;
this.tempMountains = mountains;
return new Peak(peak).save(); //make new peak from variable passed into funciton and save in db peak collection
})
.then(peak => {
this.tempMountains.peaks.push(peak._id); // puts the peak ID into array in Mountains
this.tempPeak = peak; //saves a temp copy of the peak
return this.tempMountains.save(); //database updated with new peak info!
})
.then( () => { return this.tempPeak; } ) //returns peak value to original function
.catch(next);
};
12 changes: 12 additions & 0 deletions lab-yana/model/peak.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
'use strict';

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

const peakSchema = Schema({
name: { type: String, required: true },
content: { type: String, required: true },
mountainsID: {type: Schema.Types.ObjectId, required: true } //this property is how the Mountains will know to put Peak in the "peak" array
});

module.exports = mongoose.model('peak', peakSchema); //'peak' is also used to reference to the Mountains
34 changes: 34 additions & 0 deletions lab-yana/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"name": "lab-yana",
"version": "1.0.0",
"description": "",
"main": "gulpfile.js",
"directories": {
"test": "test"
},
"scripts": {
"test": "DEBUG='peak*' mocha",
"start": "DEBUG='peak*' nodemon server.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"chai": "^3.5.0",
"gulp": "^3.9.1",
"gulp-eslint": "^3.0.1",
"gulp-mocha": "^4.0.1",
"mocha": "^3.2.0",
"superagent": "^3.5.0"
},
"dependencies": {
"bluebird": "^3.4.7",
"body-parser": "^1.17.0",
"cors": "^2.8.1",
"debug": "^2.6.1",
"express": "^4.15.0",
"http-errors": "^1.6.1",
"mongoose": "^4.8.5",
"morgan": "^1.8.1"
}
}
43 changes: 43 additions & 0 deletions lab-yana/route/mountains-route.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
'use strict';

const debug = require('debug')('peak:mountains-route');
const parseJSON = require('body-parser').json();
const Mountains = require('../model/mountains.js');
const Router = require('express').Router;
const createError = require('http-errors');

const mountainsRouter = module.exports = new Router();

mountainsRouter.get('/api/mountains/:id', function(req, res, next) { //retrieve apeak
debug('GET: /api/mountains/:id');
Mountains.findById(req.params.id)
.populate('peaks') //this puts all the the peak bodies into the peaks array property of Mountains... I think
.then(mountains => res.json(mountains)) //return list of peaks because it's a get method! they asked for it!
.catch(next);
});

mountainsRouter.post('/api/mountains', parseJSON, function(req, res, next) { //create a new Mountains object
debug('POST: /api/mountains');
req.body.timestamp = new Date();
new Mountains(req.body).save()
.then(mountains => res.json(mountains))
.catch(next);
});

mountainsRouter.put('/api/mountains/:id', parseJSON, function(req, res, next) { //updates info of a PEAK in Mountains
debug('PUT: /api/mountains/:id');
if (req._body !== true) var invalidBody = true;
Mountains.findByIdAndUpdate(req.params.id, req.body, { new: true } )//new property determines whether the new info is displayed
.then(peak => {
if (invalidBody) return Promise.reject(createError(400, 'bad request'));
res.json(peak);
})
.catch(next);
});

mountainsRouter.delete('/api/mountains/:id', function(req, res, next) { //removes Mountains
debug('DELETE: /api/mountains/:id');
Mountains.findByIdAndRemove(req.params.id)
.then( () => res.status(204).send())
.catch(next);
});
42 changes: 42 additions & 0 deletions lab-yana/route/peak-route.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
'use strict';

const debug = require('debug')('peak:peak-route.js');
const jsonParser = require('body-parser').json();
const Router = require('express').Router;
const Mountains = require('../model/mountains.js');
const Peak = require('../model/peak.js');
const createError = require('http-errors');


const peakRouter = module.exports = new Router();

peakRouter.post('/api/mountains/:mountainsID/peak', jsonParser, function(req, res, next) { //update existing peak info in Mountains
debug('POST: /api/mountains/:mountainsID/peak');
Mountains.findByIdAndAddPeak(req.params.mountainsID, req.body, { new: true } )
.then(peak => res.json(peak))
.catch(next);
});

peakRouter.get('/api/peak/:id', function(req, res, next) {
debug('GET: /api/peak/:id');
Peak.findById(req.params.id)
.then(peak => res.json(peak))
.catch(next);
});

peakRouter.delete('/api/peak/:id', function(req, res, next) {
debug('DELETE: /api/peak/:id');
Peak.findByIdAndRemove(req.params.id)
.then( () => res.status(204).send())
.catch(next);
});

peakRouter.put('/api/peak/:id', jsonParser, function(req, res, next) {
debug('PUT: /api/peak/:id');
Peak.findByIdAndUpdate(req.params.id, req.body, { new: true })
.then(peak => {
if (req._body !== true) return Promise.reject(createError(400, 'bad request'));
res.json(peak);
})
.catch(next);
});
27 changes: 27 additions & 0 deletions lab-yana/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
'use strict';

const express = require('express');
const cors = require('cors');
const debug = require('debug')('peak:server');
const morgan = require('morgan');
const Promise = require('bluebird');
const mongoose = require('mongoose');
const errors = require('./lib/error-middleware.js');
const mountainsRouter = require('./route/mountains-route.js');
const peakRouter = require('./route/peak-route.js');
const PORT = 3003;
const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://localhost/peakapp';


mongoose.Promise = Promise;
mongoose.connect(MONGODB_URI);

const app = express();

app.use(cors()); //make sure to CALL cors!!!
app.use(morgan('dev'));
app.use(mountainsRouter);
app.use(peakRouter);
app.use(errors);

app.listen(PORT, () => debug(`listening on port ${PORT}`));
Loading