diff --git a/lab-khalid/.eslintrc b/lab-khalid/.eslintrc new file mode 100644 index 0000000..8dc6807 --- /dev/null +++ b/lab-khalid/.eslintrc @@ -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" +} diff --git a/lab-khalid/.gitignore b/lab-khalid/.gitignore new file mode 100644 index 0000000..46e5ad0 --- /dev/null +++ b/lab-khalid/.gitignore @@ -0,0 +1,127 @@ +# Created by https://www.gitignore.io/api/node,vim,macos,linux,windows + +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-w][a-z] +[._]s[a-w][a-z] +# session +Session.vim +# temporary +.netrwhist +*~ +# auto-generated tag files +tags + + +### 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 + + +### 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* + + +### Windows ### +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk diff --git a/lab-khalid/README.md b/lab-khalid/README.md new file mode 100644 index 0000000..3483dc4 --- /dev/null +++ b/lab-khalid/README.md @@ -0,0 +1,20 @@ +### How to use the app + +To use the app you must first go to the root directory and run ``` npm start``` +That should run the server, and listen for route calls. Then you make any http route call to the server. You make calls to the server by writing: + +http [method] localhost:[your port or 3000]/[the route]. + + If the route is not recognized it will default to 404 not found. The routes you can use are: + 1. /api/employees + +the methods you can use are: + 1. GET + 2. POST + 3. DELETE + +A good example of a GET request is + +localhost/api/employee/89y2792742 + +That request would get us an employee with the id 89y2792742; diff --git a/lab-khalid/data/employee/fae5a610-fd64-11e6-9063-c5dea529baff.json b/lab-khalid/data/employee/fae5a610-fd64-11e6-9063-c5dea529baff.json new file mode 100644 index 0000000..b332574 --- /dev/null +++ b/lab-khalid/data/employee/fae5a610-fd64-11e6-9063-c5dea529baff.json @@ -0,0 +1 @@ +{"name":"khalid","title":"Dev","id":"fae5a610-fd64-11e6-9063-c5dea529baff"} \ No newline at end of file diff --git a/lab-khalid/gulpfile.js b/lab-khalid/gulpfile.js new file mode 100644 index 0000000..b16e376 --- /dev/null +++ b/lab-khalid/gulpfile.js @@ -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(){ + 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']); diff --git a/lab-khalid/lib/storage.js b/lab-khalid/lib/storage.js new file mode 100644 index 0000000..94eebf9 --- /dev/null +++ b/lab-khalid/lib/storage.js @@ -0,0 +1,42 @@ +'use strict'; +const debug = require('debug')('employee:storage'); +const createError = require('http-errors'); +const Promise = require('bluebird'); +const fs = Promise.promisifyAll(require('fs'), {suffix:'Prom'}); + + + +module.exports = exports = {}; + +exports.createItem = function(schemaname, item){ + debug('createItem '); + if(!schemaname) return Promise.reject(createError(400, 'Schemaname not provided')); + if(!item) return Promise.reject(createError(400, 'Item not provided')); + 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, 'Schemaname not provided')); + if(!id) return Promise.reject(createError(400, 'Id not provided')); + 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, 'Schemaname not provided')); + if(!id) return Promise.reject(createError(400, 'Id not provided')); + return fs.unlinkProm(`${__dirname}/../data/${schemaname}/${id}.json`) +} diff --git a/lab-khalid/model/employee.js b/lab-khalid/model/employee.js new file mode 100644 index 0000000..535ed1a --- /dev/null +++ b/lab-khalid/model/employee.js @@ -0,0 +1,34 @@ +'use strict'; + +const uuid = require('node-uuid'); +const debug = require('debug')('employee:employee'); +const createError = require('http-errors'); +const storage = require('../lib/storage.js'); + +const Employee = module.exports = function(name, title){ + debug('Employee constructor'); + if(!name) throw createError(400, 'name not provided'); + if(!title) throw createError(400, 'title not provided'); + + this.name = name; + this.title = title; + this.id = uuid.v1(); +}; + +Employee.createEmployee = function(_employee){ + debug('createEmployee'); + try{ + let employee = new Employee(_employee.name, _employee.title); + return storage.createItem('employee', employee); + } catch(err){ + return Promise.reject(err); + } +}; +Employee.fetchEmployee = function(id){ + debug('fetchEmployee'); + return storage.fetchItem('employee', id); +} +Employee.deleteEmployee = function(id){ + debug('deleteEmployee'); + return storage.deleteItem('employee', id); +} diff --git a/lab-khalid/package.json b/lab-khalid/package.json new file mode 100644 index 0000000..3bfcbe6 --- /dev/null +++ b/lab-khalid/package.json @@ -0,0 +1,23 @@ +{ + "name": "lab-khalid", + "version": "1.0.0", + "description": "", + "main": "server.js", + "scripts": { + "start": "DEBUG='employee*' node server.js" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "bluebird": "^3.4.7", + "body-parser": "^1.16.1", + "chai": "^3.5.0", + "debug": "^2.6.1", + "express": "^4.14.1", + "http-errors": "^1.6.1", + "morgan": "^1.8.1", + "node-uuid": "^1.4.7", + "superagent": "^3.5.0" + } +} diff --git a/lab-khalid/server.js b/lab-khalid/server.js new file mode 100644 index 0000000..0f24ad0 --- /dev/null +++ b/lab-khalid/server.js @@ -0,0 +1,68 @@ +'use strict'; + +const express = require('express'); +const app = express(); +const PORT = process.env.PORT || 3000; +const debug = require('debug')('employee:server'); +const morgan = require('morgan'); +const Employee = require('./model/employee.js'); +const jsonParser = require('body-parser').json(); +const createError = require('http-errors'); + +app.use(morgan('dev')); + +app.get('/test', function(req, res) { + debug('GET: /test'); + res.json({ msg: 'test route worked' }); +}); + +app.post('/api/employee', jsonParser, function(req,res, next){ + debug('POST /api/employee'); + Employee.createEmployee(req.body) + .then(employee => res.json(employee)) + .catch(err => next(err)); +}); + +app.get('/api/employee/:id', function(req, res, next){ + debug('GET /api/employee'); + console.log('<<<<<<<', req.params.id, '>>>>>>>>>>>') + if(req.params.id === null) { + res.status(400).send('Bad request'); + // next(); + return; + } + Employee.fetchEmployee(req.params.id) + .then(data => res.json(data)) + .catch(err => next(err)); +}); +app.get('/api/employee', function(req, res, next){ + debug('GET /api/employee/no id'); + // if(req.url.query.id) return; + res.status(400).send('Bad request'); + // next(); +}) +app.delete('/api/employee/:id', function(req, res, next){ + debug('DELETE /api/employee'); + Employee.deleteEmployee(req.params.id); + res.status(204).send(`deleted the employee with the id ${req.params.id}`); + // next(); +}); +app.use('*', function(req, res, next){ + debug('the default 404 response'); + res.status(404).send('Route not found'); +}) + +app.use(function(err, req, res){ + debug('Error middleware'); + console.error(err.message); + if(err.status){ + res.status(err.status).send(err.name); + return; + } + + err = createError(500, err.message); + res.status(err.status).send(err.name); +}); + + +app.listen(PORT, () => debug('Server up at port ', PORT)); diff --git a/lab-khalid/test/test_employee_routes.js b/lab-khalid/test/test_employee_routes.js new file mode 100644 index 0000000..177e300 --- /dev/null +++ b/lab-khalid/test/test_employee_routes.js @@ -0,0 +1,74 @@ +'use strict'; + +const expect = require('chai').expect; +const request = require('superagent'); +require('../server.js'); + +describe('Employee routes', function(){ + var employee = null; + describe('GET /api/fakeroute', function(){ + it('should return a 404', function(done){ + request.get('localhost:8000/api/fakeroute/374734736473') + .end((err, res) => { + expect(!!err).to.equal(true); + expect(res.status).to.equal(404); + done(); + }) + }); + }); + describe('GET /api/employee', function(){ + it('should return a 404', function(done){ + request.get('localhost:8000/api/employee/7293928374928374') + .end((err, res) => { + expect(!!err).to.equal(true); + expect(res.status).to.equal(404); + done(); + }) + }) + it('should return a 400', function(done){ + request.get('localhost:8000/api/employee'). + end((err, res) => { + expect(!!err).to.equal(true); + expect(res.status).to.equal(400); + done(); + }) + }) + }) + describe('POST /api/employee', function(){ + it('should return 400', function(done){ + request.post('localhost:8000/api/employee') + .end((err, res) => { + expect(!!err).to.equal(true); + expect(res.status).to.equal(400); + done(); + }) + }) + }) + describe('POST /api/employee', function(){ + it('should create an employee', function(done){ + request.post('localhost:8000/api/employee') + .send({name: 'khalid', title:'Dev'}) + .end((err, res) => { + if(err) return (err); + expect(res.status).to.equal(200); + expect(res.body.name).to.equal('khalid'); + expect(res.body.title).to.equal('Dev'); + employee = res.body; + console.log(employee.name); + done(); + }) + }) + }) + describe('GET /api/employee', function(){ + it('should get an employee', function(done){ + request.get(`localhost:8000/api/employee/${employee.id}`) + .end((err, res) => { + if(err) return done(err); + expect(res.status).to.equal(200); + expect(res.body.name).to.equal('khalid'); + expect(res.body.title).to.equal('Dev'); + done(); + }) + }) + }) +})