diff --git a/README.md b/README.md index d2d87c3..8fa190a 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,21 @@ +##Junaid's Coding Challenge Submission for Skyfit## +This submission is for the coding challenge listed below. + +###How is the application run? + +This is an express.js application. Please complete the following steps to run the app: + +1. git clone this repository and this branch. +2. From the root of the code-challenge directory run: npm install +3. From the root of the code-challenge directory run: npm run +4. /listCommits -> Lists the recent commits in raw JSON format. +5. /authorCommits -> Lists all output as requested by the challenge. + +###How are the unit tests run? + +1. mocha /code-challenge/test/test.js + + ## NodeJS Programming Task In order to be considered for the NodeJS position, you must complete the following steps. diff --git a/code-challenge/app.js b/code-challenge/app.js new file mode 100644 index 0000000..80a3c36 --- /dev/null +++ b/code-challenge/app.js @@ -0,0 +1,60 @@ +var express = require('express'); +var path = require('path'); +var favicon = require('serve-favicon'); +var logger = require('morgan'); +var cookieParser = require('cookie-parser'); +var bodyParser = require('body-parser'); + +var routes = require('./routes/index'); +var users = require('./routes/users'); + +var app = express(); + +// view engine setup +app.set('views', path.join(__dirname, 'views')); +app.set('view engine', 'jade'); + +// uncomment after placing your favicon in /public +//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); +app.use(logger('dev')); +app.use(bodyParser.json()); +app.use(bodyParser.urlencoded({ extended: false })); +app.use(cookieParser()); +app.use(express.static(path.join(__dirname, 'public'))); + +app.use('/', routes); +app.use('/users', users); + +// catch 404 and forward to error handler +app.use(function(req, res, next) { + var err = new Error('Not Found'); + err.status = 404; + next(err); +}); + +// error handlers + +// development error handler +// will print stacktrace +if (app.get('env') === 'development') { + app.use(function(err, req, res, next) { + res.status(err.status || 500); + res.render('error', { + message: err.message, + error: err + }); + }); +} + +// production error handler +// no stacktraces leaked to user +app.use(function(err, req, res, next) { + res.status(err.status || 500); + res.render('error', { + message: err.message, + error: {} + }); +}); + + +module.exports = app; diff --git a/code-challenge/bin/www b/code-challenge/bin/www new file mode 100755 index 0000000..c7321eb --- /dev/null +++ b/code-challenge/bin/www @@ -0,0 +1,90 @@ +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +var app = require('../app'); +var debug = require('debug')('code-challenge:server'); +var http = require('http'); + +/** + * Get port from environment and store in Express. + */ + +var port = normalizePort(process.env.PORT || '3000'); +app.set('port', port); + +/** + * Create HTTP server. + */ + +var server = http.createServer(app); + +/** + * Listen on provided port, on all network interfaces. + */ + +server.listen(port); +server.on('error', onError); +server.on('listening', onListening); + +/** + * Normalize a port into a number, string, or false. + */ + +function normalizePort(val) { + var port = parseInt(val, 10); + + if (isNaN(port)) { + // named pipe + return val; + } + + if (port >= 0) { + // port number + return port; + } + + return false; +} + +/** + * Event listener for HTTP server "error" event. + */ + +function onError(error) { + if (error.syscall !== 'listen') { + throw error; + } + + var bind = typeof port === 'string' + ? 'Pipe ' + port + : 'Port ' + port; + + // handle specific listen errors with friendly messages + switch (error.code) { + case 'EACCES': + console.error(bind + ' requires elevated privileges'); + process.exit(1); + break; + case 'EADDRINUSE': + console.error(bind + ' is already in use'); + process.exit(1); + break; + default: + throw error; + } +} + +/** + * Event listener for HTTP server "listening" event. + */ + +function onListening() { + var addr = server.address(); + var bind = typeof addr === 'string' + ? 'pipe ' + addr + : 'port ' + addr.port; + debug('Listening on ' + bind); +} diff --git a/code-challenge/package.json b/code-challenge/package.json new file mode 100644 index 0000000..a6159a4 --- /dev/null +++ b/code-challenge/package.json @@ -0,0 +1,23 @@ +{ + "name": "code-challenge", + "version": "0.0.0", + "private": true, + "scripts": { + "start": "node ./bin/www" + }, + "dependencies": { + "bluebird": "^3.4.6", + "body-parser": "~1.15.1", + "cookie-parser": "~1.4.3", + "debug": "~2.2.0", + "express": "~4.13.4", + "jade": "~1.11.0", + "morgan": "~1.7.0", + "serve-favicon": "~2.3.0" + }, + "devDependencies": { + "github-api": "^2.3.0", + "mocha": "^3.1.2", + "supertest": "^2.0.0" + } +} diff --git a/code-challenge/public/stylesheets/style.css b/code-challenge/public/stylesheets/style.css new file mode 100644 index 0000000..b2bbc8e --- /dev/null +++ b/code-challenge/public/stylesheets/style.css @@ -0,0 +1,13 @@ +body { + padding: 50px; + font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; +} + +a { + color: #00B7FF; +} +#numberResponse { + + color: #E6F1F6; +} + diff --git a/code-challenge/routes/index.js b/code-challenge/routes/index.js new file mode 100644 index 0000000..623e7ab --- /dev/null +++ b/code-challenge/routes/index.js @@ -0,0 +1,86 @@ +var express = require('express'); +var router = express.Router(); +var path = require('path'); + +/* GET home page. */ +router.get('/', function(req, res, next) { + res.render('index', { title: 'Express' }); + }); + +//This route lists the recent commits on the repository. +router.get('/listAllCommits', function(req, res, next) { + + + var GitHub = require('github-api'); + + // token auth + var gh = new GitHub({ +//token: 'Not needed for a public repository' +}); + + var repo = gh.getRepo('nodejs', 'node'); + console.log(repo); + + + repo.listCommits() + .then(function({data: commitsJson}) { + //console.log(commitsJson) + res.jsonp(commitsJson) + }); + + +}); + + +//This route does the following: +//1. Lists the last 25 commit SHAs on the node repository by author. +//2. Displays commit SHAs that end with a number and commit SHAs that do not end with a number. +//3. SHAs ending with a number are color coded. +router.get('/authorCommits', function(req, res, next) { + +//Connect to Github via token authentication. + + var GitHub = require('github-api'); + + // token auth + var gh = new GitHub({ +//token: 'Not needed for a public repository' +}); + +//Access the nodejs repo directly. +var repo = gh.getRepo('nodejs', 'node'); +console.log(repo); +repoParsed = JSON.stringify(repo) + +//1. List all commits that belong to user: trott +//2. Check to see which SHA ends with an integer and which SHA does not. + +repo.listCommits({author: 'trott'}) +.then(function({data: authorCommitsJson}) { + + var re = new RegExp('([0-9]+)$'); + var numberResponse = []; + var nonNumberResponse = []; + for (var i=0; i<25; i++) { + + if(authorCommitsJson[i].sha.match(re)) { + + + numberResponse.push("\n" + authorCommitsJson[i].sha) + } + else { + + nonNumberResponse.push("\n" + authorCommitsJson[i].sha); + + + } + +} +//Render the results in HTML: + res.render('index', {title: 'Junaid\'s Code Challenge for Skyfit!', repo: repoParsed, nonNumberResponse: nonNumberResponse, numberResponse: numberResponse}); + +}); +}); + + +module.exports = router; diff --git a/code-challenge/routes/npm-debug.log b/code-challenge/routes/npm-debug.log new file mode 100644 index 0000000..30b654e --- /dev/null +++ b/code-challenge/routes/npm-debug.log @@ -0,0 +1,45 @@ +0 info it worked if it ends with ok +1 verbose cli [ '/usr/bin/nodejs', '/usr/bin/npm', 'start' ] +2 info using npm@3.10.3 +3 info using node@v6.4.0 +4 verbose run-script [ 'prestart', 'start', 'poststart' ] +5 info lifecycle code-challenge@0.0.0~prestart: code-challenge@0.0.0 +6 silly lifecycle code-challenge@0.0.0~prestart: no script for prestart, continuing +7 info lifecycle code-challenge@0.0.0~start: code-challenge@0.0.0 +8 verbose lifecycle code-challenge@0.0.0~start: unsafe-perm in lifecycle true +9 verbose lifecycle code-challenge@0.0.0~start: PATH: /usr/lib/node_modules/npm/bin/node-gyp-bin:/home/hellroaster/example-nodejs-challenge/code-challenge/node_modules/.bin:/usr/bin:/usr/local/heroku/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/usr/lib/jvm/java-8-oracle/bin:/usr/lib/jvm/java-8-oracle/db/bin:/usr/lib/jvm/java-8-oracle/jre/bin +10 verbose lifecycle code-challenge@0.0.0~start: CWD: /home/hellroaster/example-nodejs-challenge/code-challenge +11 silly lifecycle code-challenge@0.0.0~start: Args: [ '-c', 'node ./bin/www' ] +12 silly lifecycle code-challenge@0.0.0~start: Returned: code: 1 signal: null +13 info lifecycle code-challenge@0.0.0~start: Failed to exec start script +14 verbose stack Error: code-challenge@0.0.0 start: `node ./bin/www` +14 verbose stack Exit status 1 +14 verbose stack at EventEmitter. (/usr/lib/node_modules/npm/lib/utils/lifecycle.js:242:16) +14 verbose stack at emitTwo (events.js:106:13) +14 verbose stack at EventEmitter.emit (events.js:191:7) +14 verbose stack at ChildProcess. (/usr/lib/node_modules/npm/lib/utils/spawn.js:40:14) +14 verbose stack at emitTwo (events.js:106:13) +14 verbose stack at ChildProcess.emit (events.js:191:7) +14 verbose stack at maybeClose (internal/child_process.js:852:16) +14 verbose stack at Process.ChildProcess._handle.onexit (internal/child_process.js:215:5) +15 verbose pkgid code-challenge@0.0.0 +16 verbose cwd /home/hellroaster/example-nodejs-challenge/code-challenge/routes +17 error Linux 3.19.0-51-generic +18 error argv "/usr/bin/nodejs" "/usr/bin/npm" "start" +19 error node v6.4.0 +20 error npm v3.10.3 +21 error code ELIFECYCLE +22 error code-challenge@0.0.0 start: `node ./bin/www` +22 error Exit status 1 +23 error Failed at the code-challenge@0.0.0 start script 'node ./bin/www'. +23 error Make sure you have the latest version of node.js and npm installed. +23 error If you do, this is most likely a problem with the code-challenge package, +23 error not with npm itself. +23 error Tell the author that this fails on your system: +23 error node ./bin/www +23 error You can get information on how to open an issue for this project with: +23 error npm bugs code-challenge +23 error Or if that isn't available, you can get their info via: +23 error npm owner ls code-challenge +23 error There is likely additional logging output above. +24 verbose exit [ 1, true ] diff --git a/code-challenge/routes/users.js b/code-challenge/routes/users.js new file mode 100644 index 0000000..623e430 --- /dev/null +++ b/code-challenge/routes/users.js @@ -0,0 +1,9 @@ +var express = require('express'); +var router = express.Router(); + +/* GET users listing. */ +router.get('/', function(req, res, next) { + res.send('respond with a resource'); +}); + +module.exports = router; diff --git a/code-challenge/test/test.js b/code-challenge/test/test.js new file mode 100644 index 0000000..0de4932 --- /dev/null +++ b/code-challenge/test/test.js @@ -0,0 +1,109 @@ +var request = require('supertest'); +var express = require('express'); +var router = express.Router(); +var path = require('path'); + +/* GET home page. */ +router.get('/', function(req, res, next) { + res.render('index', { title: 'Express' }); + }); + +//This route lists the recent commits on the repository. +router.get('/listAllCommits', function(req, res, next) { + + + var GitHub = require('github-api'); + + // token auth + var gh = new GitHub({ +token: '1b41b2d7603033d7c6e4f640573937733452ac08' +}); + + var repo = gh.getRepo('junaidkaps', 'node'); + console.log(repo); + + + repo.listCommits() + .then(function({data: commitsJson}) { + //console.log(commitsJson) + res.jsonp(commitsJson) + }); + + +}); + + +//This route does the following: +//1. Lists the last 25 commit SHAs on the node repository by author. +//2. Displays commit SHAs that end with a number and commit SHAs that do not end with a number. +//3. SHAs ending with a number are color coded. +router.get('/authorCommits', function(req, res, next) { + +//Connect to Github via token authentication. + + var GitHub = require('github-api'); + + // token auth + var gh = new GitHub({ +token: '1b41b2d7603033d7c6e4f640573937733452ac08' +}); + + +//Access the forked node repository. +var repo = gh.getRepo('junaidkaps', 'node'); +console.log(repo); + +//1. List all commits that belong to user: trott +//2. Check to see which SHA ends with an integer and which SHA does not. + +repo.listCommits({author: 'trott'}) +.then(function({data: authorCommitsJson}) { + + var re = new RegExp('([0-9]+)$'); + var numberResponse = []; + var nonNumberResponse = []; + for (var i=0; i<25; i++) { + + if(authorCommitsJson[i].sha.match(re)) { + + + numberResponse.push("\n" + authorCommitsJson[i].sha) + } + else { + + nonNumberResponse.push("\n" + authorCommitsJson[i].sha); + + + } + +} +//Render the results in HTML: + res.render('index', {title: 'Junaid\'s Code Challenge ', nonNumberResponse: nonNumberResponse, numberResponse: numberResponse}); + +}); +}); + + +module.exports = router; + +describe('GET /authorCommits', function() { + it('looking for 200 response', function(done) { + request(router) + .get('/authorCommits') + .expect(200); + done(); + }); +}); + +describe('Color Code Verification', function() { + + var url = "http://localhost:3000/authorCommits" + + it("returns color code #E6F1F6", function(done) { + request(url, function(error, response, body) { + console.log(body); + expect(body).to.equal("E6F1F6"); + done(); +}); +}); +}); diff --git a/code-challenge/views/error.jade b/code-challenge/views/error.jade new file mode 100644 index 0000000..51ec12c --- /dev/null +++ b/code-challenge/views/error.jade @@ -0,0 +1,6 @@ +extends layout + +block content + h1= message + h2= error.status + pre #{error.stack} diff --git a/code-challenge/views/index.jade b/code-challenge/views/index.jade new file mode 100644 index 0000000..276096a --- /dev/null +++ b/code-challenge/views/index.jade @@ -0,0 +1,13 @@ +extends layout + +block content + h5 Welcome to #{title} + p This is the general repository information as obtained from the Github API: + p #{repo} + p The following is a list of the last 25 SHAs belonging to user 'trott' from the repository node/nodejs - formerly known as joyent/node. (Raw JSON for all recent commits can be found at the following location: /listAllCommits). + p These are SHAs ending with a letter: + p #{nonNumberResponse} + p These are SHAs ending with a number. They have been colored light blue (#E6F1F6): + p1 #{numberResponse} + p Unit tests can be run from the test directory using mocha: e.g mocha test.js. + diff --git a/code-challenge/views/layout.jade b/code-challenge/views/layout.jade new file mode 100644 index 0000000..af03423 --- /dev/null +++ b/code-challenge/views/layout.jade @@ -0,0 +1,13 @@ +doctype html +html + head + title= title + link(rel='stylesheet', href='/stylesheets/style.css') + style. + p1 { + + color: #E6F1F6; + + } + body + block content diff --git a/code-challenge/views/npm-debug.log b/code-challenge/views/npm-debug.log new file mode 100644 index 0000000..f55bff3 --- /dev/null +++ b/code-challenge/views/npm-debug.log @@ -0,0 +1,45 @@ +0 info it worked if it ends with ok +1 verbose cli [ '/usr/bin/nodejs', '/usr/bin/npm', 'start' ] +2 info using npm@3.10.3 +3 info using node@v6.4.0 +4 verbose run-script [ 'prestart', 'start', 'poststart' ] +5 info lifecycle code-challenge@0.0.0~prestart: code-challenge@0.0.0 +6 silly lifecycle code-challenge@0.0.0~prestart: no script for prestart, continuing +7 info lifecycle code-challenge@0.0.0~start: code-challenge@0.0.0 +8 verbose lifecycle code-challenge@0.0.0~start: unsafe-perm in lifecycle true +9 verbose lifecycle code-challenge@0.0.0~start: PATH: /usr/lib/node_modules/npm/bin/node-gyp-bin:/home/hellroaster/code-challenge/code-challenge/node_modules/.bin:/usr/bin:/usr/local/heroku/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/usr/lib/jvm/java-8-oracle/bin:/usr/lib/jvm/java-8-oracle/db/bin:/usr/lib/jvm/java-8-oracle/jre/bin +10 verbose lifecycle code-challenge@0.0.0~start: CWD: /home/hellroaster/code-challenge/code-challenge +11 silly lifecycle code-challenge@0.0.0~start: Args: [ '-c', 'node ./bin/www' ] +12 silly lifecycle code-challenge@0.0.0~start: Returned: code: 1 signal: null +13 info lifecycle code-challenge@0.0.0~start: Failed to exec start script +14 verbose stack Error: code-challenge@0.0.0 start: `node ./bin/www` +14 verbose stack Exit status 1 +14 verbose stack at EventEmitter. (/usr/lib/node_modules/npm/lib/utils/lifecycle.js:242:16) +14 verbose stack at emitTwo (events.js:106:13) +14 verbose stack at EventEmitter.emit (events.js:191:7) +14 verbose stack at ChildProcess. (/usr/lib/node_modules/npm/lib/utils/spawn.js:40:14) +14 verbose stack at emitTwo (events.js:106:13) +14 verbose stack at ChildProcess.emit (events.js:191:7) +14 verbose stack at maybeClose (internal/child_process.js:852:16) +14 verbose stack at Process.ChildProcess._handle.onexit (internal/child_process.js:215:5) +15 verbose pkgid code-challenge@0.0.0 +16 verbose cwd /home/hellroaster/code-challenge/code-challenge/views +17 error Linux 3.19.0-51-generic +18 error argv "/usr/bin/nodejs" "/usr/bin/npm" "start" +19 error node v6.4.0 +20 error npm v3.10.3 +21 error code ELIFECYCLE +22 error code-challenge@0.0.0 start: `node ./bin/www` +22 error Exit status 1 +23 error Failed at the code-challenge@0.0.0 start script 'node ./bin/www'. +23 error Make sure you have the latest version of node.js and npm installed. +23 error If you do, this is most likely a problem with the code-challenge package, +23 error not with npm itself. +23 error Tell the author that this fails on your system: +23 error node ./bin/www +23 error You can get information on how to open an issue for this project with: +23 error npm bugs code-challenge +23 error Or if that isn't available, you can get their info via: +23 error npm owner ls code-challenge +23 error There is likely additional logging output above. +24 verbose exit [ 1, true ]