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
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,10 @@
# cross-repo-contributor-list
# cross-repo-contributor-list

Small node server to collect contributors from github for an organization and expose an API which can be consumed by the frontend. Uses server caching to faster serve data.

Refer settings.js for changing parameters such as Organization, cache eviction time and Github access token.

Steps to start server:
1] Update settings.js
2] npm i
3] npm start
3 changes: 2 additions & 1 deletion src/CacheService.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ class Cache {
checkperiod: ttlSeconds * 0.2,
});
}
// get contributors from cache
get(key, gitHubApi) {
const value = this.cache.get(key);
if (value) {
return Promise.resolve(value);
}

// or get it from github
return gitHubApi.then((result) => {
this.cache.set(key, result);
return result;
Expand Down
20 changes: 13 additions & 7 deletions src/ContributorRepository.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
/*
Lower level utility methods to fetch data from github and preprocess that data
*/
const octokit = require("@octokit/rest");
const githubApi = new octokit();
const async = require('async');
const Repository = require('./Repository.js');
const Profile = require('./Profile.js');
const settings = require('./settings.js');

function getContributorsByOrg(organisation) {
return getReposByOrg(organisation).then((data) => {
function getContributorsByOrg(organization) {
return getReposByOrg(organization).then((data) => {
return async.mapLimit(data, 10, async function (repository) {
const contributorPromise = await getContributorsByRepo(organisation, repository["name"]);
const contributorPromise = await getContributorsByRepo(organization, repository["name"]);
return contributorPromise;
});
}).then(async (result) => {
Expand All @@ -27,9 +30,10 @@ function getContributorsByRepo(org, repositoryName) {
headers: {
Accept: "application/vnd.github.v3+json",
Authorization: settings.access_token,
"User-Agent": settings.organisation
"User-Agent": settings.organization
}
});
// collect all contributors for a repo, map them to Profile class
githubApi.paginate(options)
.then(data => {
data = data
Expand All @@ -43,17 +47,18 @@ function getContributorsByRepo(org, repositoryName) {
});
}

function getReposByOrg(organisation) {
function getReposByOrg(organization) {
return new Promise((resolve, reject) => {
var options = githubApi.repos.listForOrg.endpoint.merge({
org: organisation,
org: organization,
type: "all",
headers: {
Accept: "application/vnd.github.v3+json",
Authorization: settings.access_token,
"User-Agent": settings.organisation
"User-Agent": settings.organization
}
});
// collect all repos for an organization, map the data to Repositor class and return
githubApi.paginate(options)
.then(data => {
data = data
Expand All @@ -66,6 +71,7 @@ function getReposByOrg(organisation) {
});
});
}
// Flatten 2D array consisting of unique users
function preProcess(data) {
return new Promise(async (resolve, reject) => {
data = Array.prototype.concat.apply([], data);
Expand Down
13 changes: 11 additions & 2 deletions src/ContributorService.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
/*
Middle level service methods which interact with CacheService and ContributorRepository
*/
const ContributorRepository = require('./ContributorRepository');
const contributors = ContributorRepository.getContributorsByOrg;
const getContributors = ContributorRepository.getContributorsByOrg;
const CacheService = require('./CacheService.js');
const settings = require('./settings.js');

const cache = new CacheService(settings.timeToLive);

// Repopulate cache when ttl for key expires
cache.cache.on('expired', (key, value) => {
ContributorService.flushAll();
console.info("Cache flushed automatically on expiration for key ", key);
Expand All @@ -14,7 +18,12 @@ cache.cache.on('expired', (key, value) => {
});
const ContributorService = {
getContributorByOrganisation(organisationName) {
return cache.get(organisationName, contributors(organisationName).then(result=>{
/*
Data is fetched from Repository layer if not found in cache.
getContributors(organisationName) is lower level Repository method, which
can be replaced with some other data source.
*/
return cache.get(organisationName, getContributors(organisationName).then(result => {
return result;
})).then(contributors => {
return contributors;
Expand Down
6 changes: 5 additions & 1 deletion src/Routes.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
/*
Top level REST APIs for data accessing
*/
const ContributorService = require('./ContributorService.js');
const settings = require('./settings.js');
const cors = require('cors');

const appRouter = function(app){

app.get('/contributors',cors(),async (req,res)=>{
res.status(200).send(await ContributorService.getContributorByOrganisation(settings.organisation));
res.status(200).send(await ContributorService.getContributorByOrganisation(settings.organization));
});

app.delete('/flushAll',cors(),(req,res)=>{
ContributorService.flushAll();
res.status(200).send("Successfully flushed the cache");
Expand Down
6 changes: 4 additions & 2 deletions src/settings.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
In order for this to work you MUST set a token. You probably want to set
an organisation and tags as well to suit you.
an organization and cache settings(time to live) as well to suit you.
=================================
1. Get a token.
Where do tokens come from?
Expand All @@ -9,9 +9,11 @@ See here: https://github.com/blog/1509-personal-api-tokens
2. Set env vars.
To set the vars on a Mac or Linux environment, run something like this in your terminal,
but replace everything after the = sign with your own content. No spaces allowed.

EXPORT CONTRIBUTORS_ACCESS_TOKEN=1234356647856878
EXPORT CONTRIBUTORS_ORG=yourgithuborghere
EXPORT CONTRIBUTORS_TTL=time_to_live_cache

optional - set the port this app is served on, e.g.:
EXPORT PORT=3333
=================================
Expand All @@ -21,7 +23,7 @@ If you prefer, you _can_ set them simply by editing this file, e.g. for const to

const token = process.env.CONTRIBUTORS_ACCESS_TOKEN;
const settings = {
"organisation": process.env.CONTRIBUTORS_ORG || "Intermine",
"organization": process.env.CONTRIBUTORS_ORG || "Intermine",
"access_token": "token "+token,
"timeToLive": process.env.CONTRIBUTORS_TTL || 1296000 //15*24*60*60 ie 15 days
}
Expand Down