Skip to content
This repository has been archived by the owner on Jun 13, 2023. It is now read-only.

Commit

Permalink
Merge pull request #120 from atsid/112-creating-teams
Browse files Browse the repository at this point in the history
112 creating teams
  • Loading branch information
natoverse committed Mar 17, 2015
2 parents 5cd1d44 + c31bf45 commit 5170fac
Show file tree
Hide file tree
Showing 22 changed files with 218 additions and 59 deletions.
14 changes: 12 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,19 @@ To run the application:

Then:

GITHUB_TOKEN=<your token> GITHUB_ORG=<your org> npm start
<environment vars> npm start

You need to use an OAuth token to authenticate with the GitHub API.
The required environment vars are:

* GITHUB_ORG: This is the org name to manage.
* GITHUB_TOKEN: You need to use an OAuth token to authenticate with the GitHub API. This token must have **owner** permissions against the org, or else team management cannot be performed.
* GITHUB_CLIENTID: This is the application OAuth ID provided by GitHub when you register the application.
* GITHUB_CLIENT_KEY: This is the application OAuth secrect key provided by GitHub when you register the application.

If you instead choose to run with mock services that don't interact with the live GitHub API, you can ignore those params and use these instead:

* SERVICE=mock: Setting 'mock' here will toggle the services to swap in a mock impl.
* GITHUB_USERNAME: Put in one of the mock usernames to simulate interaction as, because their won't be auth (testuser1, testuser2, or testuser3). Default=testuser3.

As you develop, you can pull down the latest dependencies with either `npm install` from within the Vagrant VM or `vagrant provision` from your host.

Expand Down
1 change: 1 addition & 0 deletions app/server/apps/repos/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ module.exports = {
path: '/repos/:id/users/:username/permissions/:permission',
middleware: [
auth.isAuthenticated,
permissions.createTeamForRepoPermission,
permissions.editRepoPermissionForUser,
send.noContent
]
Expand Down
1 change: 1 addition & 0 deletions app/server/apps/users/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ module.exports = {
path: '/users/:username/repos/:id/permissions/:permission',
middleware: [
auth.isAuthenticated,
permissions.createTeamForRepoPermission,
permissions.editRepoPermissionForUser,
send.noContent
]
Expand Down
61 changes: 56 additions & 5 deletions app/server/components/repositories/teams.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
var permUtil = require('./util/permissions'),
repoUtil = require('./util/repos'),
teamUtil = require('./util/teams'),
debug = require('debug')('app:repositories:teams'),
getPrefix,
getTeam;

Expand All @@ -17,28 +18,78 @@ getTeam = (teams, prefix, permission) => {

module.exports = {

edit (username, repoId, team) {

//checks if a permissive team exists to manage a given repo at specified permission
check (repoId, permission) {

return new Promise((resolve, reject) => {

debug('checking if team exists for repo [' + repoId + '] at [' + permission + ']');

repoUtil.getRepoById(repoId).then((repo) => {

let prefix = getPrefix(repo);

teamUtil.getGithubTeams().then((teams) => {

let team = getTeam(teams, prefix, permission);
resolve(typeof team !== 'undefined');

});

});

});

},

//create a team for a given repo and permission
create (repoId, permission) {

return new Promise((resolve, reject) => {

debug('creating team for [' + repoId + '] at [' + permission + ']');

repoUtil.getRepoById(repoId).then((repo) => {

teamUtil.createGithubTeamForRepoPermission(repo, permission).then((team) => {

debug('created team [', team.id + ']');
resolve(team);

}).catch((err) => {
reject(err);
});

});

});

},

edit (username, repoId, permission) {

debug('editing user [' + username + '] permission on team for repo [' + repoId + '] to permission [' + permission + ']');
// get permission map
return permUtil.getPermissionMap().then(map => {

let users = map[repoId],
current = users[username] || permUtil.getDefaultPermissions();

// user already on the team
if (current.permissive === team) {
if (current.permissive === permission) {
return;
}

// need repo name and team id
return repoUtil.getGithubRepo(repoId).then(repo => {
return repoUtil.getRepoById(repoId).then(repo => {
let prefix = getPrefix(repo);
return teamUtil.getGithubTeams().then(teams => {
let oldTeam = getTeam(teams, prefix, current.permissive),
newTeam = getTeam(teams, prefix, team);
newTeam = getTeam(teams, prefix, permission);

// remove-only, don't add to new team
if (team === 'none') {
if (permission === 'none') {
return teamUtil.removeFromGithubTeam(username, oldTeam.id);
}

Expand Down
9 changes: 5 additions & 4 deletions app/server/components/repositories/users.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
'use strict';

var userUtil = require('./util/users'),
Bluebird = require('bluebird'),
permUtil = require('./util/permissions');
Bluebird = require('bluebird');

module.exports = {

Expand All @@ -14,8 +13,10 @@ module.exports = {
},

isOrgMember (username) {
return userUtil.isOrgMember(username).then(function (data) {
return "204 No Content" === data.meta.success;
return new Promise((resolve, reject) => {
userUtil.isOrgMember(username).then(function (data) {
resolve(data.meta.status === '204 No Content');
});
});
}
};
19 changes: 19 additions & 0 deletions app/server/components/repositories/util/repos.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ convertGithubRepo = (repo) => {
return {
'id': repo.id,
'name': repo.name,
'full_name': repo.full_name,
'description': repo.description,
'public': !repo.private
};
Expand All @@ -17,6 +18,24 @@ module.exports = {
getGithubRepos () {
let args = provider.getDefaultListArgs();
return provider.github.getRepos(args).then(repos => repos.map(repo => convertGithubRepo(repo)));
},

getRepoById(repoId) {

return new Promise((resolve, reject) => {

this.getGithubRepos().then((repos) => {

//repoId is coming in as a string, hence coercion
let id = parseInt(repoId),
repo = repos.find(r => r.id === id);

resolve(repo);

});

});

}

};
12 changes: 12 additions & 0 deletions app/server/components/repositories/util/teams.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

var provider = require('./provider');

let getPrefix = (repo) => {
return 'zzz-permissive-repo-' + repo.name + '-';
};

module.exports = {

getGithubTeams () {
Expand All @@ -21,6 +25,14 @@ module.exports = {
return provider.github.getTeamRepos(args);
},

createGithubTeamForRepoPermission (repo, permission) {
let args = provider.getDefaultListArgs();
args.name = getPrefix(repo) + permission;
args.repo_names = [repo.full_name];
args.permission = permission;
return provider.github.createTeam(args);
},

addToGithubTeam(username, teamId) {
let args = provider.getDefaultItemArgs();
args.id = teamId;
Expand Down
2 changes: 1 addition & 1 deletion app/server/components/repositories/util/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ module.exports = {
isOrgMember(username) {
return provider.github.isOrgMember({
org: provider.github.config.org,
username: username
user: username
});
}
};
1 change: 1 addition & 0 deletions app/server/components/services/github.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ module.exports = {
getUser: Bluebird.promisify(github.user.getFrom),
getRepos: Bluebird.promisify(github.repos.getFromOrg),
getTeams: Bluebird.promisify(github.orgs.getTeams),
createTeam: Bluebird.promisify(github.orgs.createTeam),
getTeamMembers: Bluebird.promisify(github.orgs.getTeamMembers),
getTeamRepos: Bluebird.promisify(github.orgs.getTeamRepos),
addTeamMember: Bluebird.promisify(github.orgs.addTeamMember),
Expand Down
39 changes: 37 additions & 2 deletions app/server/components/services/github.mock.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
//jscs:disable disallowDanglingUnderscores
var mask = require('json-mask'),
debug = require('debug')('app:services:github-mock'),
org = 'testorg',
users = {
'testuser1': {
login: 'testuser1',
Expand All @@ -38,12 +39,14 @@ var mask = require('json-mask'),
'1': {
id: 1,
name: 'Test-Repo-1',
full_name: org + '/' + 'Test-Repo-1',
description: 'A library for managing file io.',
private: false
},
'2': {
id: 2,
name: 'Test-Repo-2',
full_name: org + '/' + 'Test-Repo-2',
private: true
}
},
Expand Down Expand Up @@ -75,13 +78,20 @@ var mask = require('json-mask'),
permission: 'admin',
_repos: [1],
_users: ['testuser3']
},
'5': {
id: 5,
name: 'zzz-permissive-repo-Test-Repo-2-admin',
permission: 'admin',
_repos: [2],
_users: ['testuser3']
}
};

module.exports = {

config: {
org: process.env.GITHUB_ORG
org: org
},

getUsers () {
Expand Down Expand Up @@ -129,6 +139,31 @@ module.exports = {
});
},

createTeam (msg) {
debug('creating new mock team ' + msg.name);
return new Promise((resolve, reject) => {
let repo,
id = Math.round(Math.random() * 100000),
team = {
id: id,
name: msg.name,
permission: msg.permission,
_users: []
};

//createTeam API method gets a list of repo full names - need to map that to a mock id
Object.keys(repos).forEach((key) => {
let r = repos[key];
if (r.full_name === msg.repo_names[0]) {
repo = r;
}
});
team._repos = [repo.id];
teams[id] = team;
resolve(team);
});
},

getTeamMembers (msg) {
let id = msg.id;
debug('looking up mock team [' + id + '] members');
Expand Down Expand Up @@ -206,7 +241,7 @@ module.exports = {
isOrgMember (msg) {
return new Promise((resolve, reject) => {
resolve({ meta: {
success: "204 No Content"
status: '204 No Content'
}});
});
}
Expand Down
6 changes: 3 additions & 3 deletions app/server/it/TestRepos.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ describe('Repo model HTTP requests', function () {
chai.assert.ok(items[0].id);
});

//TODO: use authenticated user to check for links
it('repo has no links', () => {
chai.assert.isUndefined(items[0].links);
//mock user is testuser3, who has admin permission
it('repo has links', () => {
chai.assert.ok(items[0].links);
});

});
Expand Down
6 changes: 3 additions & 3 deletions app/server/it/TestUsers.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ describe('User model HTTP requests', function () {
chai.assert.ok(items[0].username);
});

//TODO: use authenticated user to check for links
it('user has no links', () => {
chai.assert.isUndefined(items[0].links);
//mock user is testuser3, who has admin permission
it('user has links', () => {
chai.assert.ok(items[0].links);
});

});
Expand Down
5 changes: 3 additions & 2 deletions app/server/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ exports.start = () => {
hostname: 'localhost'
},
github: {
clientID: process.env.GITHUB_CLIENTID,
clientSecret: process.env.GITHUB_CLIENT_KEY,
//TODO: the mock is being injected here via service var. extract this to a centralized place.
clientID: process.env.GITHUB_CLIENTID || process.env.SERVICE,
clientSecret: process.env.GITHUB_CLIENT_KEY || process.env.SERVICE,
authRoute: '/auth/github',
authCallbackRoute: '/auth/github/callback',
failureCallback: '/auth/failure'
Expand Down
12 changes: 0 additions & 12 deletions app/server/middleware/auth.js

This file was deleted.

Loading

0 comments on commit 5170fac

Please sign in to comment.