diff --git a/README.md b/README.md index 61ecb1c..a966d3c 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,12 @@ Clone (or Fork) PaperBadger and enter the directory: `git clone https://github.c For an overview of the [architecture](docs/high-level-architecture.md) of the system and other details, visit the [docs](docs/) section. +#### Badges Server +PaperBadger relies on [Badgr Server](https://github.com/concentricsky/badgr-server) to manage all actions related to +badges. You can install Badgr directly from github, or you can use this docker +[local-badgr-server](https://github.com/josmas/local-badgr-server) setup, +especially built for this project, to speed things up a little. + #### Environment variables If you would like to override the default configuration, create an `.env` file in your favourite text editor and use _default.env_ as a template (do not delete or modify _default.env_). @@ -45,11 +51,10 @@ If you would like to develop against the hosted custom badgekit-api we have runn export PORT=5000 export SESSION_SECRET=USE_SOMETHING_GOOD_LIKE_puUJjfE6QtUnYryb - # Badges - export BADGES_ENDPOINT=http://badgekit-api-test-sciencelab.herokuapp.com/ - export BADGES_KEY=master - export BADGES_SECRET=############# - export BADGES_SYSTEM=badgekit + # Badgr Server + export BADGR_ENDPOINT=http://localhost:8000/ + export BADGR_USER=############# + export BADGR_PASSWORD=############# # ORCID Auth export ORCID_AUTH_CLIENT_ID=############# @@ -58,8 +63,9 @@ If you would like to develop against the hosted custom badgekit-api we have runn export ORCID_AUTH_TOKEN_PATH=############# export ORCID_REDIRECT_URI=############# -Ask [@acabunoc](http://github.com/acabunoc) for ones marked `###########`. Our custom BadgeKit API code can be found [here](https://github.com/acabunoc/badgekit-api). Feel free to change `PORT` to any available port. +Note that you will have to provide your own [Badgr](https://github.com/concentricsky/badgr-server) server, and register +for a development account with [ORCID](http://orcid.org/). #### Run using Docker diff --git a/default.env b/default.env index 032246e..9c79bd2 100644 --- a/default.env +++ b/default.env @@ -4,22 +4,24 @@ export PORT=5000 export SESSION_SECRET=USE_SOMETHING_GOOD_LIKE_puUJjfE6QtUnYryb -# Badges -export BADGES_ENDPOINT=http://badgekit-api-test-sciencelab.herokuapp.com/ -export BADGES_KEY=master -export BADGES_SECRET=############# -export BADGES_SYSTEM=badgekit +# Badgr Server +export BADGR_ENDPOINT=http://localhost:8000/ +export BADGR_USER=############# +export BADGR_PASSWORD=############# # ORCID Auth export ORCID_AUTH_CLIENT_ID=############# export ORCID_AUTH_CLIENT_SECRET=############# export ORCID_AUTH_SITE=https://orcid.org -export ORCID_AUTH_TOKEN_PATH=https://orcid.org/oauth/token +export ORCID_AUTH_TOKEN_PATH=https://pub.orcid.org/oauth/token export ORCID_REDIRECT_URI=http://localhost:5000/orcid_auth_callback # MongoDB export MONGOLAB_URI=mongodb://127.0.0.1:27017/test +# Redis +export REDISCLOUD_URL=redis://127.0.0.1:6379/0 + # Email export AWS_ACCESS_KEY= export AWS_SECRET_KEY= diff --git a/package.json b/package.json index b9fdfaf..b3e2c79 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "build:js": "webpack --config webpack.config.js --progress --profile --colors", "watch:js": "npm run build:js -- --watch", "clean": "rm -r public/js/*", - "server": "node src/index.js", + "server": "nodemon src/index.js", "eslint": "eslint --config .eslintrc.yaml src webpack.config.js public/widgets/widget.js", "eslint:test": "eslint --config .eslintrc.yaml test", "lint": "npm run eslint && npm run eslint:test", @@ -21,6 +21,7 @@ "dependencies": { "babel-loader": "^5.0.0", "badgekit-api-client": "https://github.com/mozilla/badgekit-api-client/tarball/v0.2.4", + "badgr-client": "1.0.0", "body-parser": "^1.13.2", "connect-redis": "^3.0.1", "es5-shim": "^4.1.1", @@ -29,26 +30,27 @@ "express-session": "~1.11.2", "habitat": "^3.1.2", "imports-loader": "^0.6.4", - "pug": "^2.0.0-alpha8", "jsx-loader": "^0.13.2", "mofo-style": "^2.3.0", "mongoose": "^4.1.4", "nodemailer": "^1.4.0", "nodemailer-ses-transport": "^1.3.0", "npm-run-all": "^1.2.4", + "pug": "^2.0.0-alpha8", "react": "^0.13.2", "react-checkbox-group": "^0.1.9", "react-router": "^0.13.3", "shortid": "^2.2.2", "simple-oauth2": "0.5.1", + "validator": "^5.2.0", "webpack": "^1.8.11", - "whatwg-fetch": "^0.9.0", - "validator": "^5.2.0" + "whatwg-fetch": "^0.9.0" }, "devDependencies": { "eslint": "^2.3.0", "mocha": "^2.2.5", "nock": "^2.3.0", + "nodemon": "1.10.2", "supertest": "^1.0.1" }, "engines": { diff --git a/src/app.js b/src/app.js index e47f91f..cc51c67 100755 --- a/src/app.js +++ b/src/app.js @@ -132,12 +132,12 @@ app.get('/orcid_auth_callback', function (request, response) { } else { // Token Page request.session.orcid_token_error = oauth2.accessToken.create(result); - response.redirect('/orcid_token_error'); + response.redirect('/denied'); } } else { // Token Page request.session.orcid_token = oauth2.accessToken.create(result); - response.redirect(request.session.redirect || '/issue/'); + response.redirect('/issue/'); } }); }); diff --git a/src/badges/client/index.js b/src/badges/client/index.js index ccbdfb9..8dcefac 100644 --- a/src/badges/client/index.js +++ b/src/badges/client/index.js @@ -1,12 +1,12 @@ 'use strict'; -var ApiClient = require('badgekit-api-client'); +var ApiClient = require('badgr-client'); module.exports = function (config) { var auth = { - key: config.get('BADGES_KEY'), - secret: config.get('BADGES_SECRET') + username: config.get('BADGR_USER'), + password: config.get('BADGR_PASSWORD') }; - return new ApiClient(config.get('BADGES_ENDPOINT'), auth); + return new ApiClient(config.get('BADGR_ENDPOINT'), auth); }; diff --git a/src/badges/service/index.js b/src/badges/service/index.js index deba47b..85772fc 100644 --- a/src/badges/service/index.js +++ b/src/badges/service/index.js @@ -1,27 +1,26 @@ +/* eslint camelcase: ["error", {properties: "never"}]*/ 'use strict'; var path = require('path'); var helpers = require(path.join(process.cwd(), 'src', 'helpers')); // We really want a singleton here -var instance, client, system; +var instance, client; function BadgeService() {} -BadgeService.prototype.createBadge = function (orcid, badge, dois, name) { +BadgeService.prototype.createBadge = function (orcid, badge, dois) { return function (callback) { - var evidence = helpers.urlFromDOI(dois._1, dois._2); - var context = { - system: system, - badge: badge, - instance: { - email: helpers.emailFromORCID(orcid), - evidenceUrl: evidence, - authorName: name - } + + var options = { + issuerSlug: 'mozilla-science' // TODO Can we move this value to the library? }; + options.badgeSlug = badge; + options.recipient_identifier = helpers.emailFromORCID(orcid); + options.evidence = helpers.urlFromDOI(dois._1, dois._2); + options.create_notification = false; - client.createBadgeInstance(context, function (err, badgeResult) { + client.createBadgeInstance(options, function (err, badgeResult) { if (err) { console.error(err); return callback(err); @@ -34,56 +33,45 @@ BadgeService.prototype.createBadge = function (orcid, badge, dois, name) { BadgeService.prototype.getBadges = function (orcid, badge, dois) { return function (callback) { + + var options = { + issuerSlug: 'mozilla-science' // TODO Can we move this value to the library? + }; + + // The client takes care of all the filtering, depending on the flags sent (evidence, badge, and recipient) var evidenceUrl = dois ? helpers.urlFromDOI(dois._1, dois._2) : null; + if (evidenceUrl) { + options.evidence = evidenceUrl; + } + + if (orcid) { + options.recipient = helpers.emailFromORCID(orcid); + } + + if(badge) { + options.badgeSlug = badge; + } var clientCallback = function (err, badges) { - var filtered; if (err) { console.error(err); return callback(err); } - // filter for the badge - if (badges) { - filtered = badges.filter(function (entry) { - var goodBadge = (!badge || entry.badge.slug === badge); - var goodDoi = (!dois || entry.evidenceUrl === evidenceUrl); - return goodBadge && goodDoi; - }); - - filtered = filtered.map(helpers.modEntry); - } - - if (filtered && filtered.length === 0) { + if (badges && badges.length === 0) { callback('client return empty result'); } else { - callback(null, filtered); + callback(null, badges); } }; - var context = { - system: system - }; - var options = {}; - if (orcid) { - options.email = helpers.emailFromORCID(orcid); - } else { - context.badge = badge || '*'; - } - if (evidenceUrl) { - options.paginate = { - evidenceUrl: evidenceUrl - }; - } - client.getBadgeInstances(context, options, clientCallback); + client.getBadgeInstances(options, clientCallback); }; }; BadgeService.prototype.getAllBadges = function () { return function (callback) { - client.getAllBadges({ - system: system - }, function (err, badges) { + client.getAllBadges(function (err, badges) { if (err) { console.error(err); callback(err); @@ -95,9 +83,8 @@ BadgeService.prototype.getAllBadges = function () { }; module.exports = { - init: function (apiClient, config) { + init: function (apiClient) { client = apiClient; - system = config.get('BADGES_SYSTEM'); }, getInstance: function () { diff --git a/src/helpers.js b/src/helpers.js index 40be167..2374ae0 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -38,10 +38,16 @@ function DOIFromURL(url) { return m[1]; } +function badgeClassFromURL(url) { + var badgeClass = url.split('/'); + return badgeClass[badgeClass.length - 1]; +} + module.exports = { emailFromORCID: emailFromORCID, ORCIDFromEmail: ORCIDFromEmail, modEntry: modEntry, urlFromDOI: urlFromDOI, - DOIFromURL: DOIFromURL + DOIFromURL: DOIFromURL, + badgeClassFromURL: badgeClassFromURL }; diff --git a/src/routes/papers/index.js b/src/routes/papers/index.js index 9710639..c9758aa 100644 --- a/src/routes/papers/index.js +++ b/src/routes/papers/index.js @@ -192,7 +192,6 @@ function createBadges(request, response) { response.status(400).end(); return; } - var name = request.session.orcid_token.token.name; var badges = request.body.badges || [request.param.badge]; // Delete the claim code once it's been used @@ -204,7 +203,7 @@ function createBadges(request, response) { var getTheBadges = badgerService.createBadge(request.params.orcid, badge, { '_1': request.params.doi1, '_2': request.params.doi2 - }, name); + }); getTheBadges(function (error, aBadge) { if (error !== null) { console.log('Get error from return Badges ' + error); diff --git a/templates/components/badge.jsx b/templates/components/badge.jsx index fa59c4d..e4854eb 100644 --- a/templates/components/badge.jsx +++ b/templates/components/badge.jsx @@ -8,7 +8,7 @@ var Badge = React.createClass({ return (
@@ -16,4 +16,4 @@ var Badge = React.createClass({ } }); -module.exports = Badge; \ No newline at end of file +module.exports = Badge; diff --git a/templates/components/badgeInstance.jsx b/templates/components/badgeInstance.jsx index 9b219a6..3075697 100644 --- a/templates/components/badgeInstance.jsx +++ b/templates/components/badgeInstance.jsx @@ -2,23 +2,39 @@ var React = require('react'); var BadgeInstance = React.createClass({ + + // The following two functions also exist in helpers.js in the node codebase. + badgeClassFromURL: function(url) { + var badgeClass = url.split('/'); + return badgeClass[badgeClass.length - 1]; + }, + + ORCIDFromEmail: function(email) { + var orcidRe = /(\d{4}-\d{4}-\d{4}-\d{3}[\dX])@orcid\.org/; + var m = orcidRe.exec(email); + if (m !== null) { + return m[1]; + } + }, + render: function() { var badgeInstance = this.props.badge, doiRe = /(10\.\d{3}\d+)\/(.*)\b/, - m = doiRe.exec(badgeInstance.evidenceUrl), + m = doiRe.exec(badgeInstance.json.evidence), paperUrl = '/v/#/papers/' + m[1] + '/' + encodeURIComponent(m[2]) + '/badges'; return ( ); } }); -module.exports = BadgeInstance; \ No newline at end of file +module.exports = BadgeInstance; diff --git a/test/integration/app.js b/test/integration/app.js index a40f714..b62ef23 100644 --- a/test/integration/app.js +++ b/test/integration/app.js @@ -2,6 +2,8 @@ var request = require('supertest'); var assert = require('assert'); +var path = require('path'); +var helpers = require(path.join(process.cwd(), 'src', 'helpers')); var testEnv = require('../../src/environments'); @@ -47,19 +49,18 @@ describe('Integration test against the real Badge server', function () { it('get all badge instances of a certain badge', function (done) { request(app) - .get('/badges/formal_analysis') + .get('/badges/software') .expect('Content-Type', /json/) .expect(function (res) { assert.ok(res.body[0].slug, 'not find badge slug in json'); - assert.equal(res.body[0].badge.name, 'Formal analysis'); - // assert.equal(res.body[0].email, null); ?? bug?? + assert.equal(helpers.badgeClassFromURL(res.body[0].badge_class), 'software'); }) .expect(200, done); }); it('get a count of all badge instances of a certain badge', function (done) { request(app) - .get('/badges/formal_analysis/count') + .get('/badges/software/count') .expect(function (res) { assert.ok(res.body > 0, 'no badges found'); }) @@ -72,7 +73,7 @@ describe('Integration test against the real Badge server', function () { .expect('Content-Type', /json/) .expect(function (res) { assert.ok(res.body[0].slug, 'not find one badge slug in json'); - assert.equal(res.body[0].orcid, '0000-0003-4959-3049'); + assert.equal(helpers.ORCIDFromEmail(res.body[0].recipient_identifier), '0000-0003-4959-3049'); }) .expect(200, done); }); @@ -88,18 +89,18 @@ describe('Integration test against the real Badge server', function () { it('get all badge instances of a certain badge earned by a user', function (done) { request(app) - .get('/users/0000-0003-4959-3049/badges/writing_review') + .get('/users/0000-0003-4959-3049/badges/software') .expect(function (res) { assert.ok(res.body[0].slug, 'not find one badge slug in json'); - assert.equal(res.body[0].badge.name, 'Writing - review & editing'); - assert.equal(res.body[0].orcid, '0000-0003-4959-3049'); + assert.equal(helpers.badgeClassFromURL(res.body[0].badge_class), 'software'); + assert.equal(helpers.ORCIDFromEmail(res.body[0].recipient_identifier), '0000-0003-4959-3049'); }) .expect(200, done); }); it('get a count of all badge instances of a certain badge earned by a user', function (done) { request(app) - .get('/users/0000-0003-4959-3049/badges/writing_review/count') + .get('/users/0000-0003-4959-3049/badges/software/count') .expect(function (res) { assert.ok(res.body > 0, 'no badges found'); }) @@ -111,8 +112,7 @@ describe('Integration test against the real Badge server', function () { .get('/papers/10.1186/2047-217X-2-10/badges/') .expect(function (res) { assert.ok(res.body[0].slug, 'not find one badge slug in json'); - assert.equal(res.body[0].badge.name, 'Data curation'); - assert.equal(res.body[0].evidenceUrl, 'http://dx.doi.org/10.1186/2047-217X-2-10'); + assert.equal(res.body[0].json.evidence, 'http://dx.doi.org/10.1186/2047-217X-2-10'); }) .expect(200, done); }); @@ -131,8 +131,8 @@ describe('Integration test against the real Badge server', function () { .get('/papers/10.1186/2047-217X-2-10/badges/investigation') .expect(function (res) { assert.ok(res.body[0].slug, 'not find one badge slug in json'); - assert.equal(res.body[0].badge.name, 'Investigation'); - assert.equal(res.body[0].evidenceUrl, 'http://dx.doi.org/10.1186/2047-217X-2-10'); + assert.equal(helpers.badgeClassFromURL(res.body[0].badge_class), 'investigation'); + assert.equal(res.body[0].json.evidence, 'http://dx.doi.org/10.1186/2047-217X-2-10'); }) .expect(200, done); }); @@ -151,8 +151,8 @@ describe('Integration test against the real Badge server', function () { .get('/papers/10.1186/2047-217X-2-10/users/0000-0002-3881-294X/badges') .expect(function (res) { assert.ok(res.body[0].slug, 'not find one badge slug in json'); - assert.equal(res.body[0].orcid, '0000-0002-3881-294X'); - assert.equal(res.body[0].evidenceUrl, 'http://dx.doi.org/10.1186/2047-217X-2-10'); + assert.equal(helpers.ORCIDFromEmail(res.body[0].recipient_identifier), '0000-0002-3881-294X'); + assert.equal(res.body[0].json.evidence, 'http://dx.doi.org/10.1186/2047-217X-2-10'); }) .expect(200, done); }); @@ -171,9 +171,8 @@ describe('Integration test against the real Badge server', function () { .get('/papers/10.1186/2047-217X-2-10/users/0000-0002-3881-294X/badges/investigation') .expect(function (res) { assert.ok(res.body[0].slug, 'not find one badge slug in json'); - assert.equal(res.body[0].badge.name, 'Investigation'); - assert.equal(res.body[0].orcid, '0000-0002-3881-294X'); - assert.equal(res.body[0].evidenceUrl, 'http://dx.doi.org/10.1186/2047-217X-2-10'); + assert.equal(helpers.ORCIDFromEmail(res.body[0].recipient_identifier), '0000-0002-3881-294X'); + assert.equal(res.body[0].json.evidence, 'http://dx.doi.org/10.1186/2047-217X-2-10'); }) .expect(200, done); }); diff --git a/test/unit/helpers.js b/test/unit/helpers.js index e9e6ae5..c45d8a0 100644 --- a/test/unit/helpers.js +++ b/test/unit/helpers.js @@ -48,4 +48,9 @@ describe('helpers', function () { assert.equal(helpers.DOIFromURL('http://dx.doi.org/10.1186/2047-217X-2-10/'), '10.1186/2047-217X-2-10'); assert.equal(helpers.DOIFromURL('https://dx.doi.org/10.1186/2047-217X-2-10'), '10.1186/2047-217X-2-10'); }); + + + it('returns a badge class from the end of a given a URI', function () { + assert.equal(helpers.badgeClassFromURL('http://192.168.99.100:8000/public/badges/software'), 'software'); + }); });