From 85db8ed25dda412df5760b2d8e55f3c694c972f1 Mon Sep 17 00:00:00 2001 From: Scott Rice Date: Wed, 4 Feb 2015 14:20:05 -0800 Subject: [PATCH 1/2] (#1) Display repo information in user search results. --- client/app/main/main.controller.js | 18 ---- client/app/main/main.controller.spec.js | 9 +- client/app/search/search.controller.js | 9 +- client/app/search/search.controller.spec.js | 1 + client/app/search/search.html | 14 ++- client/app/skills/skills.controller.js | 18 ++-- client/app/skills/skills.controller.spec.js | 13 +-- client/components/auth/user.service.js | 3 +- karma.conf.js | 2 +- package.json | 3 +- server/api/skills/index.js | 16 ++++ server/api/skills/skill.model.js | 13 +++ server/api/skills/skills.controller.js | 68 +++++++++++++ server/api/skills/skills.spec.js | 20 ++++ server/api/user/user.controller.js | 100 +++++++++++++++----- server/routes.js | 1 + 16 files changed, 231 insertions(+), 77 deletions(-) create mode 100644 server/api/skills/index.js create mode 100644 server/api/skills/skill.model.js create mode 100644 server/api/skills/skills.controller.js create mode 100644 server/api/skills/skills.spec.js diff --git a/client/app/main/main.controller.js b/client/app/main/main.controller.js index fc40105..fde2e86 100644 --- a/client/app/main/main.controller.js +++ b/client/app/main/main.controller.js @@ -2,27 +2,9 @@ angular.module('tikrApp') .controller('MainCtrl', function ($scope, $http, $window, Auth) { - $scope.awesomeThings = []; - - $http.get('/api/things').success(function(awesomeThings) { - $scope.awesomeThings = awesomeThings; - }); - $scope.isLoggedIn = Auth.isLoggedIn; - $scope.addThing = function() { - if($scope.newThing === '') { - return; - } - $http.post('/api/things', { name: $scope.newThing }); - $scope.newThing = ''; - }; - $scope.loginOauth = function(provider) { $window.location.href = '/auth/' + provider; }; - - $scope.deleteThing = function(thing) { - $http.delete('/api/things/' + thing._id); - }; }); diff --git a/client/app/main/main.controller.spec.js b/client/app/main/main.controller.spec.js index 1cfcc34..ab50dc0 100644 --- a/client/app/main/main.controller.spec.js +++ b/client/app/main/main.controller.spec.js @@ -6,15 +6,10 @@ describe('Controller: MainCtrl', function () { beforeEach(module('tikrApp')); var MainCtrl, - scope, - $httpBackend; + scope; // Initialize the controller and a mock scope beforeEach(inject(function (_$httpBackend_, $controller, $rootScope) { - $httpBackend = _$httpBackend_; - $httpBackend.expectGET('/api/things') - .respond(['HTML5 Boilerplate', 'AngularJS', 'Karma', 'Express']); - scope = $rootScope.$new(); MainCtrl = $controller('MainCtrl', { $scope: scope @@ -22,7 +17,5 @@ describe('Controller: MainCtrl', function () { })); it('should attach a list of things to the scope', function () { - $httpBackend.flush(); - expect(scope.awesomeThings.length).toBe(4); }); }); diff --git a/client/app/search/search.controller.js b/client/app/search/search.controller.js index bb33328..0ddf43e 100644 --- a/client/app/search/search.controller.js +++ b/client/app/search/search.controller.js @@ -1,14 +1,15 @@ 'use strict'; angular.module('tikrApp') - .controller('SearchCtrl', function($scope, $http, $q, User) { + .controller('SearchCtrl', function($scope, $http, $q, User, Auth) { $scope.users = []; $scope.searchStarted = false; // returns a promise $scope.fetchUsers = function(language) { User.search({ - skill: language + skill: language, + username: $scope.TEST_USER || Auth.getCurrentUser().github.login }, function(data) { $scope.searchStarted = true; $scope.data = data[0]; @@ -22,7 +23,7 @@ angular.module('tikrApp') $scope.languages = []; $http.get('/api/languages').success(function(data) { data.forEach(function(language) { - $scope.languages.push(language.Name + " "); + $scope.languages.push(language.Name); }); if(!input){ @@ -32,7 +33,7 @@ angular.module('tikrApp') var filtered = []; $scope.languages.forEach(function(language) { if(language.toLowerCase().indexOf(input.toLowerCase()) !== -1) { - filtered.push(language + " "); + filtered.push(language); } }); diff --git a/client/app/search/search.controller.spec.js b/client/app/search/search.controller.spec.js index 1efc278..ad56a25 100644 --- a/client/app/search/search.controller.spec.js +++ b/client/app/search/search.controller.spec.js @@ -19,6 +19,7 @@ describe('Controller: SearchCtrl', function() { })); it('should be able to fetch all users by language = javascript', function() { + $scope.TEST_USER = 'scottrice10'; createController(); var searchInput = null; diff --git a/client/app/search/search.html b/client/app/search/search.html index 330d32b..b99ce87 100644 --- a/client/app/search/search.html +++ b/client/app/search/search.html @@ -48,7 +48,19 @@
Username
-
{{user.login}}
+
+ + {{user.login}} + +
+ +
Sample work
+
+ + {{repo.name}}{{$last ? '' : ', '}} + +
+
diff --git a/client/app/skills/skills.controller.js b/client/app/skills/skills.controller.js index 1709d3a..37366f0 100644 --- a/client/app/skills/skills.controller.js +++ b/client/app/skills/skills.controller.js @@ -5,10 +5,10 @@ Note: This is a template copied from main angular.module('tikrApp') .controller('SkillsCtrl', function ($scope, $http, Auth) { - $scope.awesomeThings = []; + $scope.awesomekills = []; - $http.get('/api/things').success(function(awesomeThings) { - $scope.awesomeThings = awesomeThings; + $http.get('/api/skills').success(function(awesomeSkills) { + $scope.awesomeSkills = awesomeSkills; }); $scope.isCollapsed = true; @@ -16,19 +16,19 @@ angular.module('tikrApp') $scope.isAdmin = Auth.isAdmin; $scope.getCurrentUser = Auth.getCurrentUser; - $scope.addThing = function() { - if($scope.newThing === '') { + $scope.addSkill = function() { + if($scope.newSkill === '') { return; } - $http.post('/api/things', { name: $scope.newThing }); - $scope.newThing = ''; + $http.post('/api/skills', { name: $scope.newSkills }); + $scope.newSkill = ''; }; $scope.loginOauth = function(provider) { $window.location.href = '/auth/' + provider; }; - $scope.deleteThing = function(thing) { - $http.delete('/api/things/' + thing._id); + $scope.deleteSkill = function(skill) { + $http.delete('/api/skills/' + skill._id); }; }); diff --git a/client/app/skills/skills.controller.spec.js b/client/app/skills/skills.controller.spec.js index 3543480..bc69954 100644 --- a/client/app/skills/skills.controller.spec.js +++ b/client/app/skills/skills.controller.spec.js @@ -8,14 +8,12 @@ describe('Controller: SkillsCtrl', function () { // load the controller's module beforeEach(module('tikrApp')); - var MainCtrl, - scope, - $httpBackend; + var scope, $httpBackend, SkillsCtrl; // Initialize the controller and a mock scope beforeEach(inject(function (_$httpBackend_, $controller, $rootScope) { $httpBackend = _$httpBackend_; - $httpBackend.expectGET('/api/things') + $httpBackend.expectGET('/api/skills') .respond(['HTML5 Boilerplate', 'AngularJS', 'Karma', 'Express']); scope = $rootScope.$new(); @@ -24,8 +22,7 @@ describe('Controller: SkillsCtrl', function () { }); })); - // it('should attach a list of things to the scope', function () { - // $httpBackend.flush(); - // expect(scope.awesomeThings.length).toBe(4); - // }); + it('should attach a list of skills to the scope', function () { + + }); }); diff --git a/client/components/auth/user.service.js b/client/components/auth/user.service.js index 76ed208..33a450b 100644 --- a/client/components/auth/user.service.js +++ b/client/components/auth/user.service.js @@ -24,7 +24,8 @@ angular.module('tikrApp') params: { id: 'me', controller: 'search', - skill: null + skill: null, + username: null } } }); diff --git a/karma.conf.js b/karma.conf.js index 0b7c235..8931b1b 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -36,7 +36,7 @@ module.exports = function(config) { preprocessors: { '**/*.jade': 'ng-jade2js', '**/*.html': 'html2js', - '**/*.coffee': 'coffee', + '**/*.coffee': 'coffee' }, ngHtml2JsPreprocessor: { diff --git a/package.json b/package.json index cc9f67d..10d5cf0 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,8 @@ "passport-local": "~0.1.6", "passport-twitter": "latest", "request": "*", - "serve-favicon": "~2.0.1" + "serve-favicon": "~2.0.1", + "bluebird": "*" }, "devDependencies": { "connect-livereload": "~0.4.0", diff --git a/server/api/skills/index.js b/server/api/skills/index.js new file mode 100644 index 0000000..bf73870 --- /dev/null +++ b/server/api/skills/index.js @@ -0,0 +1,16 @@ +'use strict'; + +var express = require('express'); +var controller = require('./skills.controller'); + +var router = express.Router(); + +router.get('/', controller.index); +router.get('/:id', controller.show); +router.post('/', controller.create); +router.put('/:id', controller.update); +router.patch('/:id', controller.update); +router.delete('/:id', controller.destroy); + +module.exports = router; + diff --git a/server/api/skills/skill.model.js b/server/api/skills/skill.model.js new file mode 100644 index 0000000..32815f4 --- /dev/null +++ b/server/api/skills/skill.model.js @@ -0,0 +1,13 @@ +'use strict'; + +var mongoose = require('mongoose'), + Schema = mongoose.Schema; + +var SkillSchema = new Schema({ + name: String, + info: String, + active: Boolean +}); + +module.exports = mongoose.model('Skill', SkillSchema); + diff --git a/server/api/skills/skills.controller.js b/server/api/skills/skills.controller.js new file mode 100644 index 0000000..41ff393 --- /dev/null +++ b/server/api/skills/skills.controller.js @@ -0,0 +1,68 @@ +/** + * Using Rails-like standard naming convention for endpoints. + * GET /skills -> index + * POST /skills -> create + * GET /skills/:id -> show + * PUT /skills/:id -> update + * DELETE /skills/:id -> destroy + */ + +'use strict'; + +var _ = require('lodash'); +var Skill = require('./skill.model'); + +// Get list of skills +exports.index = function(req, res) { + Skill.find(function (err, skills) { + if(err) { return handleError(res, err); } + return res.json(200, skills); + }); +}; + +// Get a single skill +exports.show = function(req, res) { + Skill.findById(req.params.id, function (err, skill) { + if(err) { return handleError(res, err); } + if(!skill) { return res.send(404); } + return res.json(skill); + }); +}; + +// Creates a new skill in the DB. +exports.create = function(req, res) { + Skill.create(req.body, function(err, skill) { + if(err) { return handleError(res, err); } + return res.json(201, skill); + }); +}; + +// Updates an existing skill in the DB. +exports.update = function(req, res) { + if(req.body._id) { delete req.body._id; } + Skill.findById(req.params.id, function (err, skill) { + if (err) { return handleError(res, err); } + if(!skill) { return res.send(404); } + var updated = _.merge(skill, req.body); + updated.save(function (err) { + if (err) { return handleError(res, err); } + return res.json(200, skill); + }); + }); +}; + +// Deletes a skill from the DB. +exports.destroy = function(req, res) { + Skill.findById(req.params.id, function (err, skill) { + if(err) { return handleError(res, err); } + if(!skill) { return res.send(404); } + skill.remove(function(err) { + if(err) { return handleError(res, err); } + return res.send(204); + }); + }); +}; + +function handleError(res, err) { + return res.send(500, err); +} diff --git a/server/api/skills/skills.spec.js b/server/api/skills/skills.spec.js new file mode 100644 index 0000000..1f33b61 --- /dev/null +++ b/server/api/skills/skills.spec.js @@ -0,0 +1,20 @@ +'use strict'; + +var should = require('should'); +var app = require('../../app'); +var request = require('supertest'); + +describe('GET /api/skills', function() { + + it('should respond with JSON array', function(done) { + request(app) + .get('/api/skills') + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res) { + if (err) return done(err); + res.body.should.be.instanceof(Array); + done(); + }); + }); +}); diff --git a/server/api/user/user.controller.js b/server/api/user/user.controller.js index 5c1e9a0..ce8402f 100644 --- a/server/api/user/user.controller.js +++ b/server/api/user/user.controller.js @@ -3,8 +3,10 @@ var User = require('./user.model'); var passport = require('passport'); var config = require('../../config/environment'); +var githubKeys = require('../../config/local.env.js'); var jwt = require('jsonwebtoken'); var request = require('request'); +var Promise = require('bluebird'); var validationError = function(res, err) { return res.json(422, err); @@ -15,7 +17,7 @@ var validationError = function(res, err) { * restriction: 'admin' */ exports.index = function(req, res) { - User.find({}, '-salt -hashedPassword', function (err, users) { + User.find({}, '-salt -hashedPassword', function(err, users) { if(err) return res.send(500, err); res.json(200, users); }); @@ -24,26 +26,26 @@ exports.index = function(req, res) { /** * Creates a new user */ -exports.create = function (req, res, next) { +exports.create = function(req, res, next) { var newUser = new User(req.body); newUser.provider = 'local'; newUser.role = 'user'; newUser.save(function(err, user) { - if (err) return validationError(res, err); - var token = jwt.sign({_id: user._id }, config.secrets.session, { expiresInMinutes: 60*5 }); - res.json({ token: token }); + if(err) return validationError(res, err); + var token = jwt.sign({_id: user._id}, config.secrets.session, {expiresInMinutes: 60 * 5}); + res.json({token: token}); }); }; /** * Get a single user */ -exports.show = function (req, res, next) { +exports.show = function(req, res, next) { var userId = req.params.id; - User.findById(userId, function (err, user) { - if (err) return next(err); - if (!user) return res.send(401); + User.findById(userId, function(err, user) { + if(err) return next(err); + if(!user) return res.send(401); console.log("LOGGING USER JSON", user); res.json(user.profile); }); @@ -68,11 +70,11 @@ exports.changePassword = function(req, res, next) { var oldPass = String(req.body.oldPassword); var newPass = String(req.body.newPassword); - User.findById(userId, function (err, user) { + User.findById(userId, function(err, user) { if(user.authenticate(oldPass)) { user.password = newPass; user.save(function(err) { - if (err) return validationError(res, err); + if(err) return validationError(res, err); res.send(200); }); } else { @@ -81,20 +83,66 @@ exports.changePassword = function(req, res, next) { }); }; +var getReposPromise = function(user, username) { + return new Promise(function(resolve, reject) { + var repoOptions = { + url: user.repos_url + "?client_id=" + githubKeys.GITHUB_ID + "&client_secret=" + githubKeys.GITHUB_SECRET + "&page=1&per_page=3", + headers: { + 'User-Agent': username + } + }; + + request(repoOptions, function(error, response, body) { + if(!error) { + user['repos'] = JSON.parse(response.body); + resolve(user) + } else { + reject(error); + } + }); + }); +}; + +var changedUsers = []; +var getUsersPromise = function(users, username) { + var promises = users.items.map(function(user) { + return getReposPromise(user, username) + .then(function(newUser) { + return newUser; + }) + .catch(function(error) { + console.log(err); + return user; + }); + }); + + changedUsers = users; + return Promise.all(promises); +}; + /** * Query for users by skills */ exports.search = function(req, res, next) { var options = { - url: 'https://api.github.com/search/users?q=+language:' + encodeURIComponent(req.body.skill), + url: 'https://api.github.com/search/users?q=+language:' + encodeURIComponent(req.body.skill) + "&page=1&per_page=10", headers: { - 'User-Agent': 'scottrice10' + 'User-Agent': req.body.username } }; - request(options , function (error, response, body) { - if (!error) { - res.send([JSON.parse(decodeURIComponent(response.body))]); + request(options, function(error, response, body) { + if(!error) { + var users = JSON.parse(decodeURIComponent(response.body)); + + getUsersPromise(users, req.body.username) + .then(function(users) { + res.send([changedUsers]); + }) + .catch(function(error) { + console.log('error getting users', error); + }); + } else { console.log(error); res.send(500); @@ -110,8 +158,8 @@ exports.me = function(req, res, next) { User.findOne({ _id: userId }, '-salt -hashedPassword', function(err, user) { // don't ever give out the password or salt - if (err) return next(err); - if (!user) return res.json(401); + if(err) return next(err); + if(!user) return res.json(401); res.json(user); }); }; @@ -123,31 +171,31 @@ exports.authCallback = function(req, res, next) { res.redirect('/'); }; -exports.getUserProfile = function(req, res, next){ +exports.getUserProfile = function(req, res, next) { User.findOne({'github.login': req.params.githubUsername}, '-salt -hashedPassword', - function(err, user){ - if (err){ + function(err, user) { + if(err) { return next(err); } - if (!user){ + if(!user) { return res.send('Could not find that profile', 404); } //console.log("THISIS THE USER DATA ON THE SERVER", user); res.json(user); - }); + }); }; -exports.postNewSkill = function(req, res, next){ +exports.postNewSkill = function(req, res, next) { //TODO verify that user authorized to add a skill on server side User.findOneAndUpdate( {'github.login': req.params.githubUsername}, {$push: {skills: req.body}}, {safe: true}, - function(err, user){ //user is the full updated user document (a js object) - if (err) { + function(err, user) { //user is the full updated user document (a js object) + if(err) { res.send(500); } else { res.json(user); diff --git a/server/routes.js b/server/routes.js index 9779165..c3dad9a 100644 --- a/server/routes.js +++ b/server/routes.js @@ -9,6 +9,7 @@ var errors = require('./components/errors'); module.exports = function(app) { // Insert routes below + app.use('/api/skills', require('./api/skills')); app.use('/api/languages', require('./api/languages')); app.use('/api/users', require('./api/user')); app.use('/api/messages', require('./api/message')); From 6a1897be0a555c95866080deebc9ac7d57c655eb Mon Sep 17 00:00:00 2001 From: Scott Rice Date: Wed, 4 Feb 2015 14:52:18 -0800 Subject: [PATCH 2/2] (#1) For Circle Ci, use environment variables for Github id and secret. --- client/app/skills/skills.html | 1 - server/api/user/user.controller.js | 11 +++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/client/app/skills/skills.html b/client/app/skills/skills.html index 34f7a44..af6b6fb 100644 --- a/client/app/skills/skills.html +++ b/client/app/skills/skills.html @@ -4,6 +4,5 @@
-

{{ getCurrentUser().name }}'s Skill Details:

diff --git a/server/api/user/user.controller.js b/server/api/user/user.controller.js index 197be24..3f8523c 100644 --- a/server/api/user/user.controller.js +++ b/server/api/user/user.controller.js @@ -3,10 +3,16 @@ var User = require('./user.model'); var passport = require('passport'); var config = require('../../config/environment'); -var githubKeys = require('../../config/local.env.js'); var jwt = require('jsonwebtoken'); var request = require('request'); var Promise = require('bluebird'); +var githubKeys; + +try { + githubKeys = require('../../config/local.env.js'); +} catch(e) { + //do nothing +} var validationError = function(res, err) { return res.json(422, err); @@ -92,7 +98,8 @@ exports.changePassword = function(req, res, next) { var getReposPromise = function(user, username) { return new Promise(function(resolve, reject) { var repoOptions = { - url: user.repos_url + "?client_id=" + githubKeys.GITHUB_ID + "&client_secret=" + githubKeys.GITHUB_SECRET + "&page=1&per_page=3", + url: user.repos_url + "?client_id=" + (process.env.GITHUB_ID || githubKeys.GITHUB_ID) + + "&client_secret=" + (process.env.GITHUB_SECRET || githubKeys.GITHUB_SECRET) + "&page=1&per_page=3", headers: { 'User-Agent': username }