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
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"
}
91 changes: 91 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Created by https://www.gitignore.io/api/node,macos,vim,git

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

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


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


### Git ###
*.orig
91 changes: 91 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
## Express Single Resource API

This is a fun project where we created refactored our Vanilla REST API to use Express.js! Woo!

### Get the Project Running

To get this project running, type the following in your command line:

1. `git clone https://github.com/brittdawn/11-express_single_resource_api.git`
2. `cd 11-express_single_resource_api.git`
3. `npm i`
4. `brew install httpie`
5. `node server.js`

You will now see the phrase "server is up: 3000" if you have not already specified a port number.

### Test the Vanilla REST API (POST)

1. Open a new terminal located at the root of this project and type `http POST localhost:3000/api/song title="meow" description="meow meow"`
2. You should get a JSON response with a description, id, and title with a `200` status code, like this example:

``` javascript
HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 86
Content-Type: application/json; charset=utf-8
Date: Tue, 20 Dec 2016 03:49:14 GMT
ETag: W/"56-eWDnWC1unrCb1RCpC08DRQ"
X-Powered-By: Express

{
"description": "meow meow",
"id": "465251f0-c667-11e6-979e-15e9bd7f18fe",
"title": "meow"
}
```

### Test the Vanilla REST API (GET)

After making a POST, you can make a GET request.

1. Copy the id from your POST request above. Add it as a querystring to your GET request, like this example: `http localhost:3000/api/song?id=465251f0-c667-11e6-979e-15e9bd7f18fe`

2. You should get a JSON response with a description, id, and title with a `200` status code, like this example:

``` javascript
HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 86
Content-Type: application/json; charset=utf-8
Date: Tue, 20 Dec 2016 03:49:38 GMT
ETag: W/"56-eWDnWC1unrCb1RCpC08DRQ"
X-Powered-By: Express

{
"description": "meow meow",
"id": "465251f0-c667-11e6-979e-15e9bd7f18fe",
"title": "meow"
}
```

### Test the Vanilla REST API (DELETE)

After making a GET or a POST, you can make a DELETE request.

1. Copy the id from your POST/GET request above. Add it as a querystring to your DELETE request, like this example: `http DELETE localhost:3000/api/song?id=465251f0-c667-11e6-979e-15e9bd7f18fe`

2. You should get a JSON response with a description, id, and title, like this example:

``` javascript
HTTP/1.1 204
Connection: keep-alive
Content-Length: 0
Content-Type: application/json
Date: Tue, 20 Dec 2016 03:49:50 GMT
X-Powered-By: Express
```

3. If you try a GET request now for the item you deleted, it should not be found. For example, with the item above: `http DELETE localhost:3000/api/song?id=465251f0-c667-11e6-979e-15e9bd7f18fe`. Now if you will get this `404` (not found) response, because you deleted the item, yo:

``` javascript
HTTP/1.1 404 Not Found
Connection: keep-alive
Content-Length: 13
Content-Type: text/html; charset=utf-8
Date: Tue, 20 Dec 2016 03:49:53 GMT
ETag: W/"d-8ImJlDOBcq5A9PkBq5sbQw"
X-Powered-By: Express

NotFoundError
```
1 change: 1 addition & 0 deletions data/song/2b1d6ef0-c663-11e6-8151-6362d73a0c82.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"id":"2b1d6ef0-c663-11e6-8151-6362d73a0c82","title":"meow","description":"meow meow"}
1 change: 1 addition & 0 deletions data/song/35631ab0-c666-11e6-a436-95d49b074ebc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"id":"35631ab0-c666-11e6-a436-95d49b074ebc","title":"meow","description":"meow meow"}
1 change: 1 addition & 0 deletions data/song/49774dc0-c669-11e6-ad46-89d8a403c76b.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"id":"49774dc0-c669-11e6-ad46-89d8a403c76b","title":"test title","description":"test description"}
1 change: 1 addition & 0 deletions data/song/67471600-c664-11e6-87b9-896cdb3073c3.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"id":"67471600-c664-11e6-87b9-896cdb3073c3","title":"meow","description":"meow meow"}
1 change: 1 addition & 0 deletions data/song/69f309e0-c669-11e6-90f6-2d65db8c1575.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"id":"69f309e0-c669-11e6-90f6-2d65db8c1575","title":"test title","description":"test description"}
1 change: 1 addition & 0 deletions data/song/7233c540-c669-11e6-82ff-ad0cf937bc7a.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"id":"7233c540-c669-11e6-82ff-ad0cf937bc7a","title":"test title","description":"test description"}
1 change: 1 addition & 0 deletions data/song/7d419fd0-c668-11e6-b91c-f7697a602373.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"id":"7d419fd0-c668-11e6-b91c-f7697a602373","title":"test title","description":"test description"}
1 change: 1 addition & 0 deletions data/song/82a92e10-c669-11e6-9ac3-8731107568fe.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"id":"82a92e10-c669-11e6-9ac3-8731107568fe","title":"test title","description":"test description"}
1 change: 1 addition & 0 deletions data/song/908eeb50-c669-11e6-bbbb-1b1c4bcc18fd.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"id":"908eeb50-c669-11e6-bbbb-1b1c4bcc18fd","title":"test title","description":"test description"}
1 change: 1 addition & 0 deletions data/song/9e84f1b0-c668-11e6-aa3d-8da8f38cd954.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"id":"9e84f1b0-c668-11e6-aa3d-8da8f38cd954","title":"test title","description":"test description"}
1 change: 1 addition & 0 deletions data/song/ac6c4020-c669-11e6-9b97-7b290993bf82.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"id":"ac6c4020-c669-11e6-9b97-7b290993bf82","title":"test title","description":"test description"}
1 change: 1 addition & 0 deletions data/song/bbe40f10-c669-11e6-bafd-6b4f864ffd1a.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"id":"bbe40f10-c669-11e6-bafd-6b4f864ffd1a","title":"test title","description":"test description"}
1 change: 1 addition & 0 deletions data/song/d13a1c10-c6dc-11e6-baaf-571a317fb498.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"id":"d13a1c10-c6dc-11e6-baaf-571a317fb498","title":"test title","description":"test description"}
1 change: 1 addition & 0 deletions data/song/d4e9b2d0-c669-11e6-a4b3-2ded427aa76c.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"id":"d4e9b2d0-c669-11e6-a4b3-2ded427aa76c","title":"test title","description":"test description"}
1 change: 1 addition & 0 deletions data/song/e9a15930-c669-11e6-aab5-151730593493.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"id":"e9a15930-c669-11e6-aab5-151730593493","title":"test title","description":"test description"}
1 change: 1 addition & 0 deletions data/song/ee6f34f0-c669-11e6-bd41-55f016c71ca6.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"id":"ee6f34f0-c669-11e6-bd41-55f016c71ca6","title":"test title","description":"test description"}
37 changes: 37 additions & 0 deletions gulpfile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
'use strict';

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

gulp.task('pre-test', function() {
return gulp.src(['./lib/*.js', './model/*.js', '!node_modules/**'])
.pipe(istanbul())
.pipe(istanbul.hookRequire());
});

gulp.task('test', ['pre-test'], function() {
gulp.src('./test/*-test.js', { read: false})
.pipe(mocha({ report: 'spec'}))
.pipe(istanbul.writeReports())
.pipe(istanbul.enforceThresholds({thresholds: {global: 90}}));
});

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']);

gulp.task('clear', function (done) {
return cache.clearAll(done);
});
48 changes: 48 additions & 0 deletions lib/storage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
'use strict';

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

module.exports = exports = {};

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

if (!schemaName) return Promise.reject(createError(400, 'expected the schema name'));
if (!item) return Promise.reject(createError(400, 'expected an 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 the schema name'));
if (!id) return Promise.reject(createError(400, 'expected an 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 the schema name'));
if (!id) return Promise.reject(createError(400, 'expected an id'));

return fs.unlinkProm(`${__dirname}/../data/${schemaName}/${id}.json`)
.catch( err => Promise.reject(createError(500, err.message)));
};
38 changes: 38 additions & 0 deletions model/song.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
'use strict';

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

const Song = module.exports = function(title, description) {
debug('song constructor');

if (!title) throw createError(400, 'expected title');
if (!description) throw createError(400, 'expected description');

this.id = uuid.v1();
this.title = title;
this.description = description;
};

Song.createSong = function(_song) {
debug('createSong');

try {
let song = new Song(_song.title, _song.description);
return storage.createItem('song', song);
} catch (err) {
return Promise.reject(err);
}
};

Song.fetchSong = function(id) {
debug('fetchSong');
return storage.fetchItem('song', id);
};

Song.deleteSong = function(id) {
debug('deleteSong');
return storage.deleteItem('song', id);
};
Loading