diff --git a/grunt/copy.js b/grunt/copy.js
index 21693b6b4..f331d5238 100644
--- a/grunt/copy.js
+++ b/grunt/copy.js
@@ -18,6 +18,15 @@ module.exports = function (grunt) {
return dest + src.replace('FileSaver.min', 'index');
}
},
+ {
+ cwd: 'node_modules/file-saver',
+ src: 'FileSaver.min.js',
+ dest: 'src/libs/file-saver/',
+ expand: true,
+ rename: function (dest, src) {
+ return dest + src.replace('FileSaver.min', 'index');
+ }
+ },
{
src: 'bower_components/lodash/dist/*',
dest: 'src/libs/lodash/',
diff --git a/src/js/modules/orcid/extension.js b/src/js/modules/orcid/extension.js
index e6bc8f507..d81169eed 100644
--- a/src/js/modules/orcid/extension.js
+++ b/src/js/modules/orcid/extension.js
@@ -165,12 +165,25 @@ define([
if (model) {
model.set('orcid', actions);
}
+
+ if (rInfo.children) {
+ _.forEach(rInfo.children, function (putcode) {
+ var childModel = _.find(self.hiddenCollection.models, function (m) {
+ return m.get('_work').getPutCode() === putcode;
+ });
+
+ if (childModel) {
+ self.removeModel(childModel);
+ }
+ });
+ }
}
if (counter === 0) {
self.trigger('orcid-update-finished');
}
});
+
recInfo.fail(function (data) {
counter -= 1;
@@ -206,10 +219,6 @@ define([
}
});
- if (counter === 0) {
- self.trigger('orcid-update-finished', docs);
- }
-
return docs;
};
@@ -247,7 +256,7 @@ define([
if (exIds.bibcode === wIds.bibcode || doi) {
m.set({
- 'source_name': w.getSourceName(),
+ 'source_name': w.sources.join('; '),
'_work': w
});
}
@@ -423,6 +432,34 @@ define([
return $dd.promise();
};
+ WidgetClass.prototype.removeModel = function (model) {
+ var idx = model.resultsIndex;
+ this.hiddenCollection.remove(model);
+ var models = this.hiddenCollection.models;
+ _.forEach(_.rest(models, idx), function (m) {
+ m.set('resultsIndex', m.get('resultsIndex') - 1);
+ m.set('indexToShow', m.get('indexToShow') - 1);
+ });
+
+ var showRange = this.model.get('showRange');
+ var range = _.range(showRange[0], showRange[1] + 1);
+ var visible = [];
+ _.forEach(range, function (i) {
+ if (models[i] && models[i].set) {
+ models[i].set('visible', true);
+ models[i].resultsIndex = i;
+ models[i].set('resultsIndex', i);
+ models[i].set('indexToShow', i + 1);
+ visible.push(models[i]);
+ }
+ });
+ this.hiddenCollection.reset(models);
+ this.collection.reset(visible);
+
+ // reset the total number of papers
+ this.model.set('totalPapers', models.length);
+ };
+
WidgetClass.prototype.onAllInternalEvents = function(ev, arg1, arg2) {
if (ev === 'childview:OrcidAction') {
var self = this;
@@ -450,7 +487,7 @@ define([
model.set({
orcid: self._getOrcidInfo(recInfo),
- 'source_name': work.getSourceName()
+ 'source_name': work.sources.join('; ')
});
self.trigger('orcidAction:' + action, model);
@@ -496,31 +533,7 @@ define([
// Remove entry from collection after delete
if (self.orcidWidget) {
- var idx = model.resultsIndex;
- self.hiddenCollection.remove(model);
- var models = self.hiddenCollection.models;
- _.forEach(_.rest(models, idx), function (m) {
- m.set('resultsIndex', m.get('resultsIndex') - 1);
- m.set('indexToShow', m.get('indexToShow') - 1);
- });
-
- var showRange = self.model.get('showRange');
- var range = _.range(showRange[0], showRange[1] + 1);
- var visible = [];
- _.forEach(range, function (i) {
- if (models[i] && models[i].set) {
- models[i].set('visible', true);
- models[i].resultsIndex = i;
- models[i].set('resultsIndex', i);
- models[i].set('indexToShow', i + 1);
- visible.push(models[i]);
- }
- });
- self.hiddenCollection.reset(models);
- self.collection.reset(visible);
-
- // reset the total number of papers
- self.model.set('totalPapers', models.length);
+ self.removeModel(model);
} else {
// reset orcid actions
model.set('orcid', self._getOrcidInfo({}));
diff --git a/src/js/modules/orcid/orcid_api.js b/src/js/modules/orcid/orcid_api.js
index c087983c3..df09b64c0 100644
--- a/src/js/modules/orcid/orcid_api.js
+++ b/src/js/modules/orcid/orcid_api.js
@@ -372,7 +372,7 @@ define([
request.done(function (profile) {
_.forEach(cache, function (promise) {
- promise.resolve(new Profile(profile));
+ promise.resolve(self._reconcileProfileWorks(profile));
});
});
@@ -384,6 +384,85 @@ define([
});
},
+ /**
+ * Reconcile the works contained in the incoming profile.
+ * Since it's possible for an ORCiD record to contain multiple sources,
+ * we have to figure out the best one to pick.
+ *
+ * The user can selected a "preferred" source, but since we can only match
+ * on items that have enough information (bibcode, doi, etc), we have to search
+ * through them all to find the best one.
+ *
+ * @param {object} rawProfile - the incoming profile
+ * @returns {Profile} - the new profile (with reconciled works)
+ */
+ _reconcileProfileWorks: function (rawProfile) {
+ /*
+ 1. Source is ADS
+ 2. Has Bibcode
+ 3. Has DOI
+ 4. Other
+ */
+ var self = this;
+ var profile = new Profile(rawProfile);
+ var works = _.map(profile.getWorksDeep(), function (work, idx) {
+ var w;
+
+ // only operate on arrays > 1
+ if (work.length > 1) {
+ var workWithBibcode, workWithDoi;
+ _.forEach(work, function (item) {
+
+ // check if the source is ADS
+ var isADS = self.isSourcedByADS(item);
+
+ // grab an array of external ids ['bibcode', 'doi', '...']
+ var exIds = item.getExternalIdType();
+ var hasBibcode = exIds.indexOf('bibcode') > -1;
+ var hasDoi = exIds.indexOf('doi') > -1;
+
+ // if it's sourced by ADS, use that one and break out of loop
+ if (isADS) {
+ w = item;
+ return false;
+ }
+
+ // grab the first one that has a bibcode
+ if (hasBibcode && !workWithBibcode) {
+ workWithBibcode = item;
+ }
+
+ // grab the first one that has a doi
+ if (hasDoi && !workWithDoi) {
+ workWithDoi = item;
+ }
+ });
+
+ // w will be defined if we found an ADS-sourced work
+ // otherwise, set the work accordingly below
+ if (!w && workWithBibcode) {
+ w = workWithBibcode;
+ } else if (!w && workWithDoi) {
+ w = workWithDoi;
+ } else if (!w) {
+ w = work[0];
+ }
+
+ // set the work's list of sources based on the full list from orcid
+ w.sources = _.map(work, function (_w) {
+ return _w.getSourceName();
+ });
+ }
+
+ // take the first work if we haven't found an array to process
+ return w ? w : work[0];
+ });
+
+ // set the new works
+ profile.setWorks(works);
+ return profile;
+ },
+
/**
* Retrieves user profile
* Must have scope: /orcid-profile/read-limited
@@ -643,12 +722,27 @@ define([
});
// on fail, reject the promises
- prom.fail(function () {
- self.addCache = _.reduce(self.addCache, function (res, entry) {
- entry.promise.state() === 'pending' ?
- entry.promise.reject() : res.push(entry);
- return res;
- }, []);
+ // this should receive a list of ids which we can finish up with
+ prom.fail(function (ids) {
+ var args = arguments;
+ _.forEach(ids, function (id) {
+
+ // find the cache entry
+ var idx = _.findIndex(self.addCache, { id: id });
+ if (idx >= 0) {
+
+ // grab reference to promise
+ var promise = self.addCache[idx].promise;
+
+ // remove entry from cache
+ self.addCache.splice(idx, idx + 1);
+
+ // if it is still pending, reject it now
+ if (promise.state() === 'pending') {
+ promise.reject.apply(promise, args);
+ }
+ }
+ });
});
},
@@ -710,9 +804,9 @@ define([
}, {});
$dd.resolve(obj);
- }, function () {
+ }, function (xhr) {
self.setDirty();
- $dd.reject.apply($dd, arguments);
+ $dd.reject.apply($dd, [xhr.cacheIds].concat(arguments));
});
return $dd.promise();
@@ -1006,8 +1100,7 @@ define([
* unset (-1) so that it won't be counted as an orcid record.
*
*/
- var querySuccess = function () {
- var ids = _.flatten(arguments);
+ var querySuccess = function (ids) {
// Update each orcid record with identifier info gained from ADS
_.each(db, function (v, key) {
@@ -1021,6 +1114,7 @@ define([
}
});
+ self._combineDatabaseWorks(db);
finishUpdate(db);
};
@@ -1047,6 +1141,41 @@ define([
return self.dbUpdatePromise.promise();
},
+ /**
+ * Looks at the identifier of the work and attempts to
+ * detect if a bibcode has a child within the other entries
+ * of the database.
+ *
+ * @param {object} db - the database object
+ * @returns {object} db - the update database object
+ */
+ _combineDatabaseWorks: function (db) {
+
+ // loop through each entry of the database
+ _.forEach(db, function (data, identifier) {
+
+ // we can only do this for entries with data and bibcodes
+ if (_.isUndefined(data) || _.isUndefined(data.bibcode)) {
+ return true;
+ }
+
+ // remove 'identifier:' from front of key
+ var key = identifier.split(':')[1];
+
+ // add an children property to the current (parent entry)
+ _.forEach(db, function (entry, subKey) {
+
+ // excluding our parent, see if the key matches the bibcode
+ if (entry.bibcode === key && subKey !== identifier) {
+ data.children = data.children || [];
+ data.children.push(entry.putcode);
+ }
+ });
+ });
+
+ return db;
+ },
+
/**
* Creates a metadata object based on the work that is passed in that
* helps with understanding the record's relationship with ADS. Figures
@@ -1102,6 +1231,10 @@ define([
}
out.putcode = rec.putcode;
out.bibcode = rec.bibcode;
+
+ if (rec.children) {
+ out.children = rec.children;
+ }
}
};
diff --git a/src/js/modules/orcid/profile.js b/src/js/modules/orcid/profile.js
index a0aeee68d..32270dc5f 100644
--- a/src/js/modules/orcid/profile.js
+++ b/src/js/modules/orcid/profile.js
@@ -19,7 +19,8 @@ define([
* @constructor
*/
var Profile = function Profile(profile) {
- this._root = profile;
+ this._root = profile || {};
+ this.works = [];
/**
* search profile for value at specified path
@@ -39,12 +40,35 @@ define([
/**
* Gets all the work summaries from the profile
+ * Shallow (only grabs the first entry)
*
* @returns {Work[]} - the array of Work summaries
*/
this.getWorks = function () {
+ return this.works;
+ };
+
+ /**
+ * Set the profile works
+ *
+ * @param {*} works
+ */
+ this.setWorks = function (works) {
+ this.works = works;
+ return this;
+ };
+
+ /**
+ * Pull all arrays of works from the profile
+ * grabs all works, not just the first
+ *
+ * @returns {[]Work[]} - the array of arrays of Work summaries
+ */
+ this.getWorksDeep = function () {
return _.map(this.getWorkSummaries(), function (w) {
- return new Work(w);
+ return _.map(w['work-summary'], function (subWork) {
+ return new Work(subWork);
+ });
});
};
@@ -100,6 +124,11 @@ define([
}
return obj;
}, this);
+
+ // to maintain old behavior, make sure works is filled when the profile is created
+ this.works = _.map(this.getWorkSummaries(), function (w) {
+ return new Work(w);
+ });
};
return Profile;
diff --git a/src/js/modules/orcid/work.js b/src/js/modules/orcid/work.js
index 446338638..6fd44141e 100644
--- a/src/js/modules/orcid/work.js
+++ b/src/js/modules/orcid/work.js
@@ -52,10 +52,25 @@ define([
* @param work
* @constructor
*/
- var Work = function Work(work) {
+ var Work = function Work(work, options) {
work = work || {};
+
+ // find the inner summary as the root
this._root = (work['work-summary']) ? work['work-summary'][0] : work;
+ var sources = '';
+ Object.defineProperty(this, 'sources', {
+ set: function (data) {
+ sources = data;
+ },
+ get: function () {
+ if (sources.length > 0) {
+ return sources;
+ }
+ return [this.getSourceName()];
+ }
+ });
+
/**
* Get the value at path
*
@@ -109,7 +124,7 @@ define([
title: [this.getTitle()],
formattedDate: this.getFormattedPubDate(),
abstract: this.getShortDescription(),
- source_name: this.getSourceName(),
+ source_name: this.sources.join('; '),
pub: this.getJournalTitle(),
_work: this
});
diff --git a/src/js/widgets/list_of_things/templates/item-template.html b/src/js/widgets/list_of_things/templates/item-template.html
index e616ab9e9..b950b6420 100644
--- a/src/js/widgets/list_of_things/templates/item-template.html
+++ b/src/js/widgets/list_of_things/templates/item-template.html
@@ -211,10 +211,12 @@
{{{title}}}
Claim in ORCID
{{else}}
-
+
+
+
+ Record not known to ADS
+
+
{{/if}}
diff --git a/test/mocha/js/modules/orcid/orcid_api.spec.js b/test/mocha/js/modules/orcid/orcid_api.spec.js
index 266623441..7709b2c5e 100644
--- a/test/mocha/js/modules/orcid/orcid_api.spec.js
+++ b/test/mocha/js/modules/orcid/orcid_api.spec.js
@@ -28,695 +28,810 @@ define([
Work,
Profile
) {
- sinon.test(function () {
- var createOrcidServer = function (orcidApi, minsub) {
- var server = sinon.fakeServer.create();
+ var createOrcidServer = function (orcidApi, minsub) {
+ var server = sinon.fakeServer.create();
- server.respondWith('GET', /\/orcid-works\/(.*)/, function (xhr, putcodes) {
- xhr.respond(200, {
- 'Content-Type': 'application/json'
- }, JSON.stringify(helpers.getMock('work')));
- });
+ server.respondWith('GET', /\/orcid-works\/(.*)/, function (xhr, putcodes) {
+ xhr.respond(200, {
+ 'Content-Type': 'application/json'
+ }, JSON.stringify(helpers.getMock('work')));
+ });
+
+ server.respondWith('POST', /\/orcid-works/, function (xhr) {
+ xhr.respond(201, {
+ 'Content-Type': 'application/json'
+ }, xhr.requestBody);
+ });
+
+ server.respondWith('PUT', /\/orcid-works\/(.*)/, function (xhr, putcodes) {
+ xhr.respond(200, {
+ 'Content-Type': 'application/json'
+ }, xhr.requestBody);
+ });
+
+ server.respondWith('DELETE', /\/orcid-works\/.*/, function (xhr) {
+ xhr.respond(204);
+ });
+
+ server.respondWith('GET', /\/orcid-profile/, function (xhr) {
+ xhr.respond(200, {
+ 'Content-Type': 'application/json'
+ }, JSON.stringify(helpers.getMock('profile')));
+ });
+
+ var sendRequest = function (url, options, data) {
+ var $dd = $.Deferred();
- server.respondWith('POST', /\/orcid-works/, function (xhr) {
- xhr.respond(201, {
+ $.ajax(url, _.extend(options, {
+ data: JSON.stringify(data),
+ dataType: 'json',
+ headers: {
'Content-Type': 'application/json'
- }, xhr.requestBody);
+ }
+ }))
+ .done(function () {
+ options && options.done && options.done.apply(this, arguments);
+ $dd.resolve.apply($dd, arguments);
+ })
+ .fail(function () {
+ options && options.fail && options.fail.apply(this, arguments);
+ $dd.reject.apply($dd, arguments);
});
- server.respondWith('PUT', /\/orcid-works\/(.*)/, function (xhr, putcodes) {
+ server.respond();
+ return $dd.promise();
+ };
+
+ sinon.stub(orcidApi, 'createRequest', function (url, options, data) {
+ var parts = url.split('/');
+ if (parts.length === 5) {
+ url = '/' + _.last(parts);
+ } else {
+ url = '/' + parts.slice(-2).join('/');
+ }
+ return sendRequest(url, options, data);
+ });
+
+ if (minsub) {
+ server.respondWith('GET', /\/exchangeOAuthCode/, [200, {
+ 'Content-Type': 'application/json'
+ }, JSON.stringify(helpers.getMock('oAuth'))]);
+
+ server.respondWith('GET', /search\/query/, function (xhr) {
xhr.respond(200, {
'Content-Type': 'application/json'
- }, xhr.requestBody);
+ }, JSON.stringify(helpers.getMock('adsResponse')));
});
- server.respondWith('DELETE', /\/orcid-works\/.*/, function (xhr) {
- xhr.respond(204);
+ sinon.stub(minsub, 'request', function (apiRequest) {
+ var url = apiRequest.get('target');
+ var options = apiRequest.get('options');
+ var data = options.data;
+ return sendRequest(url, options, data);
});
+ }
- server.respondWith('GET', /\/orcid-profile/, function (xhr) {
- xhr.respond(200, {
- 'Content-Type': 'application/json'
- }, JSON.stringify(helpers.getMock('profile')));
+ return server;
+ };
+
+ var getMinSub = function () {
+ var minsub = new MinimalPubsub({ verbose: false });
+ minsub.beehive.addObject('DynamicConfig', {
+ orcidClientId: 'APP-P5ANJTQRRTMA6GXZ',
+ orcidApiEndpoint: 'https://api.orcid.org',
+ orcidRedirectUrlBase: 'http://localhost:8000'
+ });
+ return minsub;
+ };
+
+ var getOrcidApi = function (beehive) {
+ var oModule = new OrcidModule();
+ oModule.activate(beehive);
+ var oApi = beehive.getService('OrcidApi');
+ oApi.saveAccessData({
+ "access_token":"4274a0f1-36a1-4152-9a6b-4246f166bafe",
+ "orcid":"0000-0001-8178-9506"
+ });
+ return oApi;
+ };
+
+ describe("Orcid API service (orcid_api.spec.js)", function () {
+ describe("OAuth", function() {
+ beforeEach(function (done) {
+ var minsub = new (MinimalPubsub.extend({
+ request: function (apiRequest) {
+ if (apiRequest.get('target').indexOf('/exchangeOAuthCode') > -1) {
+ expect(apiRequest.get('query').get('code')).to.eql(['secret']);
+ return {
+ "access_token":"4274a0f1-36a1-4152-9a6b-4246f166bafe",
+ "token_type":"bearer",
+ "expires_in":3599,
+ "scope":"/orcid-works/create /orcid-profile/read-limited /orcid-works/update",
+ "orcid":"0000-0001-8178-9506",
+ "name":"Roman Chyla"};
+ }
+ else if (apiRequest.get('target').indexOf('test-query') > -1) {
+ var opts = apiRequest.get('options');
+ expect(opts.headers["Orcid-Authorization"]).to.eql('Bearer 4274a0f1-36a1-4152-9a6b-4246f166bafe');
+ expect(opts.data).to.eql('{"data":{"foo":"bar"}}');
+ return {success: true};
+ }
+ }
+ }))({verbose: false});
+ minsub.beehive.addObject('DynamicConfig', {
+ orcidClientId: 'APP-P5ANJTQRRTMA6GXZ',
+ orcidApiEndpoint: 'https://api.orcid.org',
+ orcidRedirectUrlBase: 'http://localhost:8000'
+ });
+ this.minsub = minsub;
+ this.beehive = minsub.beehive;
+ done();
});
- var sendRequest = function (url, options, data) {
- var $dd = $.Deferred();
+ it("has methods to extract access code", function() {
+ var oApi = getOrcidApi(this.minsub.beehive);
+ // it receives window.location.search
+ expect(oApi.getUrlParameter('code', '?foo=bar&code=H1trXI')).to.eql('H1trXI');
+ expect(oApi.hasExchangeCode('?foo=bar&code=H1trXI')).to.eql(true);
+ expect(oApi.getExchangeCode('?foo=bar&code=H1trXI')).to.eql('H1trXI');
+ });
+
+ it("can exchange code for access_token (auth data)", function(done) {
+ var oApi = getOrcidApi(this.minsub.beehive);
+ var r = oApi.getAccessData('secret');
+ expect(_.isUndefined(r.done)).to.eql(false);
+ //this.server.respond();
+ r.done(function(res) {
+ expect(res).to.eql({
+ "access_token":"4274a0f1-36a1-4152-9a6b-4246f166bafe",
+ "token_type":"bearer",
+ "expires_in":3599,
+ "scope":"/orcid-works/create /orcid-profile/read-limited /orcid-works/update",
+ "orcid":"0000-0001-8178-9506",
+ "name":"Roman Chyla"});
+
+ oApi.saveAccessData(res);
+
+ expect(oApi.authData).to.eql(res);
+
+ // the expires was added
+ expect(oApi.authData.expires).to.be.gt(new Date().getTime());
+
+ // now request uses access_token
+ var req = oApi.sendData('test-query', {data: {foo: 'bar'}});
+ req.done(function(res) {
+ expect(res).to.eql({success: true});
+ done();
+ });
- $.ajax(url, _.extend(options, {
- data: JSON.stringify(data),
- dataType: 'json',
- headers: {
- 'Content-Type': 'application/json'
- }
- }))
- .done(function () {
- options && options.done && options.done.apply(this, arguments);
- $dd.resolve.apply($dd, arguments);
})
- .fail(function () {
- options && options.fail && options.fail.apply(this, arguments);
- $dd.reject.apply($dd, arguments);
- });
+ });
- server.respond();
- return $dd.promise();
- };
+ it("should handle ORCID sign in", function(){
- sinon.stub(orcidApi, 'createRequest', function (url, options, data) {
- var parts = url.split('/');
- if (parts.length === 5) {
- url = '/' + _.last(parts);
- } else {
- url = '/' + parts.slice(-2).join('/');
- }
- return sendRequest(url, options, data);
- });
+ var oApi = getOrcidApi(this.minsub.beehive);
- if (minsub) {
- server.respondWith('GET', /\/exchangeOAuthCode/, [200, {
- 'Content-Type': 'application/json'
- }, JSON.stringify(helpers.getMock('oAuth'))]);
+ this.beehive.getService("PubSub").publish = sinon.spy();
- server.respondWith('GET', /search\/query/, function (xhr) {
- xhr.respond(200, {
- 'Content-Type': 'application/json'
- }, JSON.stringify(helpers.getMock('adsResponse')));
- });
+ oApi.signIn();
- sinon.stub(minsub, 'request', function (apiRequest) {
- var url = apiRequest.get('target');
- var options = apiRequest.get('options');
- var data = options.data;
- return sendRequest(url, options, data);
- });
- }
+ expect(JSON.stringify(this.beehive.getService("PubSub").publish.args[0])).to.eql('[{},"[App]-Exit",{"type":"orcid","url":"undefined?scope=/orcid-profile/read-limited%20/orcid-works/create%20/orcid-works/update&response_type=code&access_type=offline&show_login=true&client_id=APP-P5ANJTQRRTMA6GXZ&redirect_uri=http%3A%2F%2Flocalhost%3A8000%2F%23%2Fuser%2Forcid"}]');
+ expect(JSON.stringify(this.beehive.getService("PubSub").publish.args[1])).to.eql('[{},"[PubSub]-Orcid-Announcement","login"]')
+ });
+
+ it("signOut forgets authentication details", function() {
+ var oApi = getOrcidApi(this.minsub.beehive);
+ expect(oApi.hasAccess()).to.be.eql(false, 'needs to have authData defined');
+ oApi.authData = {foo: 'bar'};
+ expect(oApi.hasAccess()).to.be.eql(false, 'should need to have expires');
+ oApi.signOut();
+ expect(oApi.hasAccess()).to.be.eql(false, 'sign out cleared authdata');
+
+ oApi.authData = {expires: new Date().getTime() + 100};
+ expect(oApi.hasAccess()).to.be.eql(true, 'authData was not expired');
+ oApi.authData = {expires: new Date().getTime() - 100};
+ expect(oApi.hasAccess()).to.be.eql(false, 'authData was expired');
+ });
+
+ });
- return server;
+ var isPromise = function (val) {
+ var con = val.constructor === $.Deferred().constructor;
+ var props = val.done && !val.resolve;
+ return con && props;
};
- var getMinSub = function () {
- var minsub = new MinimalPubsub({ verbose: false });
- minsub.beehive.addObject('DynamicConfig', {
- orcidClientId: 'APP-P5ANJTQRRTMA6GXZ',
- orcidApiEndpoint: 'https://api.orcid.org',
- orcidRedirectUrlBase: 'http://localhost:8000'
+ describe('addWork', function () {
+ var sb, server, oApi;
+ beforeEach(function () {
+ sb = sinon.sandbox.create();
+ var minsub = getMinSub();
+ oApi = getOrcidApi(minsub.beehive);
+ server = createOrcidServer(oApi, minsub);
+ });
+ afterEach(function () {
+ sb.restore();
+ server && server.restore && server.restore();
});
- return minsub;
- };
- var getOrcidApi = function (beehive) {
- var oModule = new OrcidModule();
- oModule.activate(beehive);
- var oApi = beehive.getService('OrcidApi');
- oApi.saveAccessData({
- "access_token":"4274a0f1-36a1-4152-9a6b-4246f166bafe",
- "orcid":"0000-0001-8178-9506"
+ it('handles bad input', function (done) {
+ expect(oApi.addWork.bind(oApi)).to.throw(TypeError);
+ expect(oApi.addWork.bind(oApi, null)).to.throw(TypeError);
+ expect(oApi.addWork.bind(oApi, [])).to.throw(TypeError);
+ done();
});
- return oApi;
- };
- describe("Orcid API service (orcid_api.spec.js)", function () {
- describe("OAuth", function() {
- beforeEach(function (done) {
- var minsub = new (MinimalPubsub.extend({
- request: function (apiRequest) {
- if (apiRequest.get('target').indexOf('/exchangeOAuthCode') > -1) {
- expect(apiRequest.get('query').get('code')).to.eql(['secret']);
- return {
- "access_token":"4274a0f1-36a1-4152-9a6b-4246f166bafe",
- "token_type":"bearer",
- "expires_in":3599,
- "scope":"/orcid-works/create /orcid-profile/read-limited /orcid-works/update",
- "orcid":"0000-0001-8178-9506",
- "name":"Roman Chyla"};
- }
- else if (apiRequest.get('target').indexOf('test-query') > -1) {
- var opts = apiRequest.get('options');
- expect(opts.headers["Orcid-Authorization"]).to.eql('Bearer 4274a0f1-36a1-4152-9a6b-4246f166bafe');
- expect(opts.data).to.eql('{"data":{"foo":"bar"}}');
- return {success: true};
- }
- }
- }))({verbose: false});
- minsub.beehive.addObject('DynamicConfig', {
- orcidClientId: 'APP-P5ANJTQRRTMA6GXZ',
- orcidApiEndpoint: 'https://api.orcid.org',
- orcidRedirectUrlBase: 'http://localhost:8000'
- });
- this.minsub = minsub;
- this.beehive = minsub.beehive;
- done();
- });
+ it('updates cache', function (done) {
+ oApi.addWork({ test: 'test' });
+ expect(oApi.addCache.length).to.eql(1);
+ expect(oApi.addCache[0].work).to.eql({ test: 'test' });
+ done();
+ });
- it("has methods to extract access code", function() {
- var oApi = getOrcidApi(this.minsub.beehive);
- // it receives window.location.search
- expect(oApi.getUrlParameter('code', '?foo=bar&code=H1trXI')).to.eql('H1trXI');
- expect(oApi.hasExchangeCode('?foo=bar&code=H1trXI')).to.eql(true);
- expect(oApi.getExchangeCode('?foo=bar&code=H1trXI')).to.eql('H1trXI');
- });
+ it('calls _addWork', function (done) {
+ sb.spy(oApi, '_addWork');
+ oApi.addWork({ test: 'test' });
+ expect(oApi._addWork.callCount).to.eql(1);
+ done();
+ });
- it("can exchange code for access_token (auth data)", function(done) {
- var oApi = getOrcidApi(this.minsub.beehive);
- var r = oApi.getAccessData('secret');
- expect(_.isUndefined(r.done)).to.eql(false);
- //this.server.respond();
- r.done(function(res) {
- expect(res).to.eql({
- "access_token":"4274a0f1-36a1-4152-9a6b-4246f166bafe",
- "token_type":"bearer",
- "expires_in":3599,
- "scope":"/orcid-works/create /orcid-profile/read-limited /orcid-works/update",
- "orcid":"0000-0001-8178-9506",
- "name":"Roman Chyla"});
-
- oApi.saveAccessData(res);
-
- expect(oApi.authData).to.eql(res);
-
- // the expires was added
- expect(oApi.authData.expires).to.be.gt(new Date().getTime());
-
- // now request uses access_token
- var req = oApi.sendData('test-query', {data: {foo: 'bar'}});
- req.done(function(res) {
- expect(res).to.eql({success: true});
- done();
- });
-
- })
- });
+ it('returns a promise', function (done) {
+ var ret = oApi.addWork({ test: 'test' });
+ expect(isPromise(ret)).to.eql(true);
+ done();
+ });
- it("should handle ORCID sign in", function(){
+ it('returned promise equals one in cache', function (done) {
+ var ret = oApi.addWork({ test: 'test' });
+ expect(isPromise(ret)).to.eql(true);
+ expect(oApi.addCache[0].promise.promise()).to.eql(ret);
+ done();
+ });
+ });
- var oApi = getOrcidApi(this.minsub.beehive);
+ describe('_addWork', function () {
+ var sb, server, oApi;
+ beforeEach(function () {
+ sb = sinon.sandbox.create();
+ var minsub = getMinSub();
+ oApi = getOrcidApi(minsub.beehive);
+ server = createOrcidServer(oApi, minsub);
+ oApi._addWork = _.debounce(OrcidApi.prototype._addWork, 10);
+ });
+ afterEach(function () {
+ sb.restore();
+ server && server.restore && server.restore();
+ });
- this.beehive.getService("PubSub").publish = sinon.spy();
+ it('calls _addWorks', function (done) {
+ sb.spy(oApi, '_addWorks');
+ oApi._addWork();
+ _.delay(function () {
+ expect(oApi._addWorks.callCount).to.eql(1);
+ done();
+ }, 15);
+ });
- oApi.signIn();
+ it('is debounced, firing only after idle period', function (done) {
+ sb.spy(oApi, '_addWorks');
+ oApi._addWork();
+ oApi._addWork();
+ oApi._addWork();
+ oApi._addWork();
+ _.delay(function () {
+ expect(oApi._addWorks.callCount).to.eql(1);
+ done();
+ }, 15);
+ });
- expect(JSON.stringify(this.beehive.getService("PubSub").publish.args[0])).to.eql('[{},"[App]-Exit",{"type":"orcid","url":"undefined?scope=/orcid-profile/read-limited%20/orcid-works/create%20/orcid-works/update&response_type=code&access_type=offline&show_login=true&client_id=APP-P5ANJTQRRTMA6GXZ&redirect_uri=http%3A%2F%2Flocalhost%3A8000%2F%23%2Fuser%2Forcid"}]');
- expect(JSON.stringify(this.beehive.getService("PubSub").publish.args[1])).to.eql('[{},"[PubSub]-Orcid-Announcement","login"]')
- });
+ it('recovers from orcid conflict', function (done) {
- it("signOut forgets authentication details", function() {
- var oApi = getOrcidApi(this.minsub.beehive);
- expect(oApi.hasAccess()).to.be.eql(false, 'needs to have authData defined');
- oApi.authData = {foo: 'bar'};
- expect(oApi.hasAccess()).to.be.eql(false, 'should need to have expires');
- oApi.signOut();
- expect(oApi.hasAccess()).to.be.eql(false, 'sign out cleared authdata');
-
- oApi.authData = {expires: new Date().getTime() + 100};
- expect(oApi.hasAccess()).to.be.eql(true, 'authData was not expired');
- oApi.authData = {expires: new Date().getTime() - 100};
- expect(oApi.hasAccess()).to.be.eql(false, 'authData was expired');
+ // returns an orcid conflict error msg
+ sb.stub(oApi, '_addWorks', function () {
+ var val = { work: { error: { 'response-code': 409 }}};
+ return $.Deferred().resolve(val).promise();
});
+ var $dd = $.Deferred();
+ oApi.addCache.push({ id: 0, work: { test: 'old' }, promise: $dd });
+ oApi._addWork();
+ $dd.done(function (work) {
+
+ // should get an object back with *old* work
+ expect(_.isPlainObject(work)).to.eql(true);
+ expect(work).to.eql({ test: 'old' });
+ done();
+ });
});
- var isPromise = function (val) {
- var con = val.constructor === $.Deferred().constructor;
- var props = val.done && !val.resolve;
- return con && props;
- };
+ it('rejects promise on orcid error (non-conflict)', function (done) {
- describe('addWork', function () {
- var sb, server, oApi;
- beforeEach(function () {
- sb = sinon.sandbox.create();
- var minsub = getMinSub();
- oApi = getOrcidApi(minsub.beehive);
- server = createOrcidServer(oApi, minsub);
- });
- afterEach(function () {
- sb.restore();
- server && server.restore && server.restore();
+ // returns an orcid generic error msg
+ sb.stub(oApi, '_addWorks', function () {
+ var val = { work: { error: {}}};
+ return $.Deferred().resolve(val).promise();
});
- it('handles bad input', function (done) {
- expect(oApi.addWork.bind(oApi)).to.throw(TypeError);
- expect(oApi.addWork.bind(oApi, null)).to.throw(TypeError);
- expect(oApi.addWork.bind(oApi, [])).to.throw(TypeError);
+ var $dd = $.Deferred();
+ oApi.addCache.push({ id: 0, work: { test: 'old' }, promise: $dd });
+ oApi._addWork();
+ $dd.done(function () { expect(false); });
+ $dd.fail(function () {
+ expect(true);
done();
});
+ });
- it('updates cache', function (done) {
- oApi.addWork({ test: 'test' });
- expect(oApi.addCache.length).to.eql(1);
- expect(oApi.addCache[0].work).to.eql({ test: 'test' });
- done();
- });
+ it('returns a Work record', function (done) {
- it('calls _addWork', function (done) {
- sb.spy(oApi, '_addWork');
- oApi.addWork({ test: 'test' });
- expect(oApi._addWork.callCount).to.eql(1);
- done();
+ // returns a *new* orcid object
+ sb.stub(oApi, '_addWorks', function () {
+ var val = { 0: { work: { test: 'new' }}};
+ return $.Deferred().resolve(val).promise();
});
- it('returns a promise', function (done) {
- var ret = oApi.addWork({ test: 'test' });
- expect(isPromise(ret)).to.eql(true);
+ var $dd = $.Deferred();
+ oApi.addCache.push({ id: 0, work: { test: 'old' }, promise: $dd });
+ oApi._addWork();
+ $dd.done(function (orcidWork) {
+
+ // should get a Work object back with *new* work
+ expect(orcidWork instanceof Work).to.eql(true);
+ expect(orcidWork._root).to.eql({ test: 'new' });
done();
});
+ });
- it('returned promise equals one in cache', function (done) {
- var ret = oApi.addWork({ test: 'test' });
- expect(isPromise(ret)).to.eql(true);
- expect(oApi.addCache[0].promise.promise()).to.eql(ret);
+ it('removes cache items as they are used', function (done) {
+ oApi.addCache = [
+ { id: 0, work: {}, promise: $.Deferred() },
+ { id: 1, work: {}, promise: $.Deferred() },
+ { id: 2, work: {}, promise: $.Deferred() }
+ ];
+ oApi._addWork();
+ _.delay(function () {
+ expect(oApi.addCache.length).to.eql(0);
done();
- });
+ }, 15);
});
- describe('_addWork', function () {
- var sb, server, oApi;
- beforeEach(function () {
- sb = sinon.sandbox.create();
- var minsub = getMinSub();
- oApi = getOrcidApi(minsub.beehive);
- server = createOrcidServer(oApi, minsub);
- oApi._addWork = _.debounce(OrcidApi.prototype._addWork, 10);
- });
- afterEach(function () {
- sb.restore();
- server && server.restore && server.restore();
- });
+ it('rejects cached promises upon request failure', function (done) {
- it('calls _addWorks', function (done) {
- sb.spy(oApi, '_addWorks');
- oApi._addWork();
- _.delay(function () {
- expect(oApi._addWorks.callCount).to.eql(1);
- done();
- }, 15);
+ // the request fails...
+ sb.stub(oApi, '_addWorks', function () {
+ var val = [0];
+ return $.Deferred().reject(val).promise();
});
- it('is debounced, firing only after idle period', function (done) {
- sb.spy(oApi, '_addWorks');
- oApi._addWork();
- oApi._addWork();
- oApi._addWork();
- oApi._addWork();
- _.delay(function () {
- expect(oApi._addWorks.callCount).to.eql(1);
- done();
- }, 15);
+ var $dd = $.Deferred();
+ oApi.addCache.push({ id: 0, work: { test: 'old' }, promise: $dd });
+ oApi._addWork();
+ $dd.fail(function (ids) {
+ expect(ids).to.eql([0]);
+ expect(oApi.addCache.length).to.eql(0);
+ done();
});
+ });
+ });
- it('recovers from orcid conflict', function (done) {
+ describe('_addWorks', function () {
+ var sb, server, oApi;
- // returns an orcid conflict error msg
- sb.stub(oApi, '_addWorks', function () {
- var val = { work: { error: { 'response-code': 409 }}};
- return $.Deferred().resolve(val).promise();
- });
+ beforeEach(function () {
+ sb = sinon.sandbox.create();
+ var minsub = getMinSub();
+ oApi = getOrcidApi(minsub.beehive);
+ server = createOrcidServer(oApi, minsub);
+ oApi._addWork = _.debounce(OrcidApi.prototype._addWork, 10);
+ });
+ afterEach(function () {
+ sb.restore();
+ });
- var $dd = $.Deferred();
- oApi.addCache.push({ id: 0, work: { test: 'old' }, promise: $dd });
- oApi._addWork();
- $dd.done(function (work) {
+ it('handles bad input', function () {
+ expect(oApi._addWorks.bind(oApi)).to.throw(TypeError);
+ expect(oApi._addWorks.bind(oApi, null, [])).to.throw(TypeError);
+ expect(oApi._addWorks.bind(oApi, [], null)).to.throw(TypeError);
+ expect(oApi._addWorks.bind(oApi, null, null)).to.throw(TypeError);
+ expect(oApi._addWorks.bind(oApi, {}, {})).to.throw(TypeError);
+ expect(oApi._addWorks.bind(oApi, {}, null)).to.throw(TypeError);
+ expect(oApi._addWorks.bind(oApi, null, {})).to.throw(TypeError);
+ });
- // should get an object back with *old* work
- expect(_.isPlainObject(work)).to.eql(true);
- expect(work).to.eql({ test: 'old' });
- done();
- });
+ it('correctly chunks input', function (done) {
+ oApi.maxAddChunkSize = 10;
+ var works = _.map(_.range(0, 100), function (i) {
+ return { title: 'test', i: i };
});
+ var ids = _.range(100, 200);
+ var prom = oApi._addWorks(works, ids);
+ prom.done(function () {
+ expect(oApi.createRequest.callCount).to.eql(10, '10 requests');
+ done();
+ });
+ });
- it('rejects promise on orcid error (non-conflict)', function (done) {
-
- // returns an orcid generic error msg
- sb.stub(oApi, '_addWorks', function () {
- var val = { work: { error: {}}};
- return $.Deferred().resolve(val).promise();
- });
+ it('request on success, resolves w/ id-indexed works', function (done) {
+ var works = _.map(_.range(0, 200), function (i) {
+ return { title: 'test', i: i };
+ });
+ var ids = _.range(100, 300);
+ var prom = oApi._addWorks(works, ids);
+ prom.done(function (res) {
- var $dd = $.Deferred();
- oApi.addCache.push({ id: 0, work: { test: 'old' }, promise: $dd });
- oApi._addWork();
- $dd.done(function () { expect(false); });
- $dd.fail(function () {
- expect(true);
- done();
+ // check the response is right
+ _.forEach(ids, function (n, i) {
+ expect(res[n].work).to.eql(works[i], 'work matches');
});
+ done();
});
+ });
- it('returns a Work record', function (done) {
-
- // returns a *new* orcid object
- sb.stub(oApi, '_addWorks', function () {
- var val = { 0: { work: { test: 'new' }}};
- return $.Deferred().resolve(val).promise();
- });
+ it.skip('request on fail, resolves promise w/ array of ids', function (done) {
- var $dd = $.Deferred();
- oApi.addCache.push({ id: 0, work: { test: 'old' }, promise: $dd });
- oApi._addWork();
- $dd.done(function (orcidWork) {
+ server.respondWith('POST', /\/orcid-works/, function (xhr) {
+ xhr.respond(404, { 'Content-Type': 'application/json' }, xhr.requestBody);
+ });
- // should get a Work object back with *new* work
- expect(orcidWork instanceof Work).to.eql(true);
- expect(orcidWork._root).to.eql({ test: 'new' });
- done();
- });
+ var ids = _.range(0, 300);
+ var works = _.map(ids, function (i) {
+ return { title: 'test', i: i };
});
+ var prom = oApi._addWorks(works, ids);
+ prom.fail(function (res) {
+ expect(_.isArray(res)).to.eql(true);
- it('removes cache items as they are used', function (done) {
- oApi.addCache = [
- { id: 0, work: {}, promise: $.Deferred() },
- { id: 1, work: {}, promise: $.Deferred() },
- { id: 2, work: {}, promise: $.Deferred() }
- ];
- oApi._addWork();
- _.delay(function () {
- expect(oApi.addCache.length).to.eql(0);
- done();
- }, 15);
+ // jquery fails on first failure
+ expect(res).to.deep.equal(ids.slice(0, 100));
+ done();
+ });
+ prom.done(function () {
+ done(new Error());
});
+ prom.always(function () {
+ done();
+ });
+ });
- it('rejects cached promises upon request failure', function (done) {
+ it('returns a promise', function () {
+ var prom = oApi._addWorks([], []);
+ expect(isPromise(prom)).to.eql(true);
+ });
+ });
- // the request fails...
- sb.stub(oApi, '_addWorks', function () {
- var val = [0];
- return $.Deferred().reject(val).promise();
- });
+ describe("Orcid Actions", function() {
- var $dd = $.Deferred();
- oApi.addCache.push({ id: 0, work: { test: 'old' }, promise: $dd });
- oApi._addWork();
- $dd.fail(function (ids) {
- expect(ids).to.eql([0]);
- expect(oApi.addCache.length).to.eql(0);
- done();
- });
- });
+ beforeEach(function () {
+ this.minsub = getMinSub();
+ this.sb = sinon.sandbox.create();
});
- describe('_addWorks', function () {
- var sb, server, oApi;
- beforeEach(function () {
- sb = sinon.sandbox.create();
- var minsub = getMinSub();
- oApi = getOrcidApi(minsub.beehive);
- server = createOrcidServer(oApi, minsub);
- oApi._addWork = _.debounce(OrcidApi.prototype._addWork, 10);
- });
- afterEach(function () {
- sb.restore();
- });
+ afterEach(function () {
+ this.sb.restore();
+ });
- it('handles bad input', function (done) {
- expect(oApi._addWorks.bind(oApi)).to.throw(TypeError);
- expect(oApi._addWorks.bind(oApi, null, [])).to.throw(TypeError);
- expect(oApi._addWorks.bind(oApi, [], null)).to.throw(TypeError);
- expect(oApi._addWorks.bind(oApi, null, null)).to.throw(TypeError);
- expect(oApi._addWorks.bind(oApi, {}, {})).to.throw(TypeError);
- expect(oApi._addWorks.bind(oApi, {}, null)).to.throw(TypeError);
- expect(oApi._addWorks.bind(oApi, null, {})).to.throw(TypeError);
- done();
- });
+ it('should be GenericModule', function (done) {
+ expect(new OrcidApi() instanceof GenericModule).to.equal(true, 'is GenericModule');
+ expect(new OrcidApi() instanceof OrcidApi).to.equal(true, 'is OrcidApi');
+ done();
+ });
- it('correctly chunks input', function (done) {
- oApi.maxAddChunkSize = 10;
- var works = _.map(_.range(0, 100), function (i) {
- return { title: 'test', i: i };
- });
- var ids = _.range(100, 200);
- var prom = oApi._addWorks(works, ids);
- prom.done(function () {
- expect(oApi.createRequest.callCount).to.eql(10, '10 requests');
- done();
+ it("exports hardened interface", function() {
+ var oApi = getOrcidApi(this.minsub.beehive);
+ var hardened = oApi.getHardenedInstance();
+ var check = function (props) {
+ _.forEach(props, function (p) {
+ expect(_.has(hardened, p)).to.equal(true, 'Hardened Interface has property ' + p);
});
- });
+ };
+
+ check([
+ 'hasAccess',
+ 'getUserProfile',
+ 'signIn',
+ 'signOut',
+ 'getADSUserData',
+ 'setADSUserData',
+ 'getRecordInfo',
+ 'addWork',
+ 'deleteWork',
+ 'updateWork',
+ 'getWork',
+ 'getWorks'
+ ]);
+ });
- it('request on success, resolves w/ id-indexed works', function (done) {
- var works = _.map(_.range(0, 200), function (i) {
- return { title: 'test', i: i };
- });
- var ids = _.range(100, 300);
- var prom = oApi._addWorks(works, ids);
- prom.done(function (res) {
-
- // check the response is right
- _.forEach(ids, function (n, i) {
- expect(res[n].work).to.eql(works[i], 'work matches');
- });
+ //TODO: throwing sinon error, extend this
+ it('getUserProfile', function (done) {
+ var oApi = getOrcidApi(this.minsub.beehive);
+ createOrcidServer(oApi, this.minsub);
+ oApi.getUserProfile()
+ .done(function (profile) {
+ expect(profile instanceof Profile).to.equal(true, 'returns instance of Profile');
done();
});
- });
-
- it('request on fail, resolves promise w/ array of ids', function (done) {
+ });
- server.respondWith('POST', /\/orcid-works/, function (xhr) {
- xhr.respond(404, {
- 'Content-Type': 'application/json'
- }, xhr.requestBody);
+ it('getWork', function (done) {
+ var oApi = getOrcidApi(this.minsub.beehive);
+ createOrcidServer(oApi, this.minsub);
+ oApi.getWork(99999)
+ .done(function (work) {
+ expect(work instanceof Work).to.equal(true, 'returns instance of Work');
+ done();
});
+ });
- var works = _.map(_.range(0, 200), function (i) {
- return { title: 'test', i: i };
+ it('deleteWork', function (done) {
+ var oApi = getOrcidApi(this.minsub.beehive);
+ createOrcidServer(oApi, this.minsub);
+ oApi.deleteWork(99999)
+ .done(function (data, status, xhr) {
+ expect(xhr.status).to.equal(204, 'get 204 status code');
+ expect(data).to.equal(undefined, 'got no content');
+ done();
});
- var ids = _.range(100, 300);
- var prom = oApi._addWorks(works, ids);
- prom.fail(function (res) {
- expect(_.isArray(res)).to.eql(true);
+ });
- // jquery fails on first failure
- expect(res).to.eql(ids.splice(0, 100));
+ it('updateWork', function (done) {
+ var oApi = getOrcidApi(this.minsub.beehive);
+ createOrcidServer(oApi, this.minsub);
+ var orcidWork = new Work(helpers.getMock('work')).getAsOrcid();
+ oApi.updateWork(orcidWork)
+ .done(function (work) {
+ expect(JSON.stringify(work)).to.equal(JSON.stringify(orcidWork),
+ 'get our updated work back');
done();
});
- });
-
- it('returns a promise', function (done) {
- var prom = oApi._addWorks([], []);
- expect(isPromise(prom)).to.eql(true);
- done();
- });
});
- describe("Orcid Actions", function() {
-
- beforeEach(function () {
- this.minsub = getMinSub();
- });
-
- it('should be GenericModule', function (done) {
- expect(new OrcidApi() instanceof GenericModule).to.equal(true, 'is GenericModule');
- expect(new OrcidApi() instanceof OrcidApi).to.equal(true, 'is OrcidApi');
- done();
- });
+ it('getRecordInfo', function () {
+ var oApi = getOrcidApi(this.minsub.beehive);
+ sinon.stub(oApi, 'needsUpdate', _.constant(false));
+
+ var db = {
+ "identifier:foo": {
+ "sourcedByADS": true,
+ "putcode": 889362,
+ "idx": 0
+ },
+ "identifier:bar": {
+ "sourcedByADS": false,
+ "putcode": 878658,
+ "idx": 1
+ },
+ "identifier:boo": {
+ "sourcedByADS": true,
+ "putcode": 880867,
+ "idx": 1
+ },
+ "identifier:baz": {
+ "sourcedByADS": false,
+ "putcode": 880867,
+ "idx": -1
+ }
+ };
- it("exports hardened interface", function() {
- var oApi = getOrcidApi(this.minsub.beehive);
- var hardened = oApi.getHardenedInstance();
- var check = function (props) {
- _.forEach(props, function (p) {
- expect(_.has(hardened, p)).to.equal(true, 'Hardened Interface has property ' + p);
- });
- };
-
- check([
- 'hasAccess',
- 'getUserProfile',
- 'signIn',
- 'signOut',
- 'getADSUserData',
- 'setADSUserData',
- 'getRecordInfo',
- 'addWork',
- 'deleteWork',
- 'updateWork',
- 'getWork',
- 'getWorks'
- ]);
- });
+ var check = function (rInfo, val, exp) {
+ expect(rInfo[val]).to.equal(exp, val + ' : ' + exp);
+ };
- //TODO: throwing sinon error, extend this
- it('getUserProfile', function (done) {
- var oApi = getOrcidApi(this.minsub.beehive);
- createOrcidServer(oApi, this.minsub);
- oApi.getUserProfile()
- .done(function (profile) {
- expect(profile instanceof Profile).to.equal(true, 'returns instance of Profile');
- done();
- });
+ oApi.db = db;
+ oApi.getRecordInfo({ bibcode: 'foo' }).done(function (rInfo) {
+ check(rInfo, 'isCreatedByADS', true);
+ check(rInfo, 'isCreatedByOthers', false);
+ check(rInfo, 'isKnownToADS', true);
});
- it('getWork', function (done) {
- var oApi = getOrcidApi(this.minsub.beehive);
- createOrcidServer(oApi, this.minsub);
- oApi.getWork(99999)
- .done(function (work) {
- expect(work instanceof Work).to.equal(true, 'returns instance of Work');
- done();
- });
+ oApi.getRecordInfo({ bibcode: 'bar' }).done(function (rInfo) {
+ check(rInfo, 'isCreatedByADS', false);
+ check(rInfo, 'isCreatedByOthers', true);
+ check(rInfo, 'isKnownToADS', true);
});
- it('deleteWork', function (done) {
- var oApi = getOrcidApi(this.minsub.beehive);
- createOrcidServer(oApi, this.minsub);
- oApi.deleteWork(99999)
- .done(function (data, status, xhr) {
- expect(xhr.status).to.equal(204, 'get 204 status code');
- expect(data).to.equal(undefined, 'got no content');
- done();
- });
+ oApi.getRecordInfo({ doi: 'boo' }).done(function (rInfo) {
+ check(rInfo, 'isCreatedByADS', true);
+ check(rInfo, 'isCreatedByOthers', false);
+ check(rInfo, 'isKnownToADS', true);
});
- it('updateWork', function (done) {
- var oApi = getOrcidApi(this.minsub.beehive);
- createOrcidServer(oApi, this.minsub);
- var orcidWork = new Work(helpers.getMock('work')).getAsOrcid();
- oApi.updateWork(orcidWork)
- .done(function (work) {
- expect(JSON.stringify(work)).to.equal(JSON.stringify(orcidWork),
- 'get our updated work back');
- done();
- });
+ oApi.getRecordInfo({ doi: 'baz' }).done(function (rInfo) {
+ check(rInfo, 'isCreatedByADS', false);
+ check(rInfo, 'isCreatedByOthers', true);
+ check(rInfo, 'isKnownToADS', false);
});
+ });
- it('getRecordInfo', function () {
- var oApi = getOrcidApi(this.minsub.beehive);
- sinon.stub(oApi, 'needsUpdate', _.constant(false));
-
- var db = {
- "identifier:foo": {
- "sourcedByADS": true,
- "putcode": 889362,
- "idx": 0
- },
- "identifier:bar": {
- "sourcedByADS": false,
- "putcode": 878658,
- "idx": 1
- },
- "identifier:boo": {
- "sourcedByADS": true,
- "putcode": 880867,
- "idx": 1
- },
- "identifier:baz": {
- "sourcedByADS": false,
- "putcode": 880867,
- "idx": -1
- }
- };
-
- var check = function (rInfo, val, exp) {
- expect(rInfo[val]).to.equal(exp, val + ' : ' + exp);
- };
-
- oApi.db = db;
- oApi.getRecordInfo({ bibcode: 'foo' }).done(function (rInfo) {
- check(rInfo, 'isCreatedByADS', true);
- check(rInfo, 'isCreatedByOthers', false);
- check(rInfo, 'isKnownToADS', true);
- });
-
- oApi.getRecordInfo({ bibcode: 'bar' }).done(function (rInfo) {
- check(rInfo, 'isCreatedByADS', false);
- check(rInfo, 'isCreatedByOthers', true);
- check(rInfo, 'isKnownToADS', true);
- });
-
- oApi.getRecordInfo({ doi: 'boo' }).done(function (rInfo) {
- check(rInfo, 'isCreatedByADS', true);
- check(rInfo, 'isCreatedByOthers', false);
- check(rInfo, 'isKnownToADS', true);
- });
+ it('detects children entries', function () {
+ var oApi = getOrcidApi(this.minsub.beehive);
+ var createMockDB = function (entries) {
+ return _.reduce(entries, function (out, e) {
+ out['identifier:' + e[0]] = {
+ bibcode: e[1],
+ putcode: e[2]
+ };
+ return out;
+ }, {});
+ };
+
+ var mockDb = createMockDB([
+ ['A', '', '1'],
+ ['B', 'A', '2'], // child of A
+ ['C', '', '3'],
+ ['D', 'A', '4'], // child of A
+ ['E', 'B', '5'], // child of B
+ ['F', 'B', '6'], // child of B
+ ['G', '', '7'],
+ ['H', 'G', '8'] // child of G
+ ]);
+
+ oApi._combineDatabaseWorks(mockDb);
+
+ expect(['2', '4']).to.deep.equal(mockDb['identifier:A'].children);
+ expect(['5', '6']).to.deep.equal(mockDb['identifier:B'].children);
+ expect(['8']).to.deep.equal(mockDb['identifier:G'].children);
+ expect(undefined).to.eql(mockDb['identifier:H'].children);
+ });
- oApi.getRecordInfo({ doi: 'baz' }).done(function (rInfo) {
- check(rInfo, 'isCreatedByADS', false);
- check(rInfo, 'isCreatedByOthers', true);
- check(rInfo, 'isKnownToADS', false);
+ it('reconcile profile works', function () {
+ var oApi = getOrcidApi(this.minsub.beehive);
+ createOrcidServer(oApi, this.minsub);
+ this.sb.stub(oApi, 'isSourcedByADS', function (work) {
+ return work.getSourceClientIdPath() === 'ADS';
+ });
+ var createMockProfile = function (groups) {
+ var out = { 'activities-summary': { 'works': {}}}
+ out['activities-summary']['works']['group'] = groups.map(function (entries) {
+ return {
+ 'work-summary': entries.map(function (entry) {
+ return {
+ 'title': { title: { value: entry.title } },
+ 'source': { 'source-client-id': { path: entry.path }, 'source-name': { value: entry.title } },
+ 'external-ids': { 'external-id': [ { 'external-id-type': entry.type } ]}
+ };
+ })
+ };
});
+ return out;
+ }
+ var mockProfile = createMockProfile([
+ [
+ { title: 'A', path: '', type: 'other' }, // <-- since there are no better choices
+ { title: 'B', path: '', type: 'other' },
+ { title: 'C', path: '', type: 'other' }
+ ],
+ [
+ { title: 'A', path: '', type: 'other' },
+ { title: 'B', path: '', type: 'other' },
+ { title: 'C', path: '', type: 'doi' } // <-- has a doi
+ ],
+ [
+ { title: 'A', path: '', type: 'other' },
+ { title: 'B', path: '', type: 'bibcode' }, // <-- has a bibcode
+ { title: 'C', path: '', type: 'other' }
+ ],
+ [
+ { title: 'A', path: '', type: 'other' },
+ { title: 'B', path: 'ADS', type: 'other' }, // <-- source is ADS
+ { title: 'C', path: '', type: 'other' }
+ ],
+ [
+ { title: 'A', path: '', type: 'other' },
+ { title: 'B', path: '', type: 'doi' },
+ { title: 'C', path: '', type: 'bibcode' } // <-- bibcode beats doi
+ ],
+ [
+ { title: 'A', path: '', type: 'doi' },
+ { title: 'B', path: '', type: 'bibcode' }, // should take the first bibcode
+ { title: 'C', path: '', type: 'bibcode' }
+ ],
+ [
+ { title: 'A', path: '', type: 'bibcode' },
+ { title: 'B', path: '', type: 'doi' },
+ { title: 'C', path: 'ADS', type: '' } // <-- ADS beats anything
+ ]
+ ]);
+
+ var profile = oApi._reconcileProfileWorks(mockProfile);
+
+ // profile should be type Profile
+ expect(profile).to.be.instanceOf(Profile);
+
+ var expected = ['A', 'C', 'B', 'B', 'C', 'B', 'C'];
+ var actual = profile.works.map(function (w) {
+ return w.getSourceName();
+ });
+
+ // check that we get the right works in the final profile
+ expect(expected).to.deep.equal(actual);
+
+ // for good measure, check the source names, to make sure an array is there
+ _.forEach(profile.works, function (w) {
+ expect(['A', 'B', 'C']).to.deep.equal(w.sources);
});
+ });
- it.skip("has methods to query status of a record", function(done) {
- var oApi = getOrcidApi(this.minsub.beehive);
- createOrcidServer(oApi, this.minsub);
- sinon.spy(oApi, 'updateDatabase');
- sinon.stub(oApi, '_checkIdsInADS', function (query) {
- return $.Deferred().resolve({
- "bibcode:2018cnsns..56..296s": "2018cnsns..56..296s",
- "doi:10.1016/j.cnsns.2017.08.013": "2018cnsns..56..296s",
- "bibcode:2018cnsns..56..270q": "2018cnsns..56..270q",
- "doi:10.1016/j.cnsns.2017.08.014": "2018cnsns..56..270q"
- }).promise();
- });
+ it.skip("has methods to query status of a record", function(done) {
+ var oApi = getOrcidApi(this.minsub.beehive);
+ createOrcidServer(oApi, this.minsub);
+ sinon.spy(oApi, 'updateDatabase');
+ sinon.stub(oApi, '_checkIdsInADS', function (query) {
+ return $.Deferred().resolve({
+ "bibcode:2018cnsns..56..296s": "2018cnsns..56..296s",
+ "doi:10.1016/j.cnsns.2017.08.013": "2018cnsns..56..296s",
+ "bibcode:2018cnsns..56..270q": "2018cnsns..56..270q",
+ "doi:10.1016/j.cnsns.2017.08.014": "2018cnsns..56..270q"
+ }).promise();
+ });
+
+ // for testing purpose, force split into many queries
+ oApi.maxQuerySize = 2;
+
+ oApi.getRecordInfo({bibcode: '2018cnsns..56..296s'})
+ .done(function (recInfo) {
+
+ expect(oApi._checkIdsInADS.called).to.eql(true);
+ expect(oApi._checkIdsInADS.calledTwice).to.eql(true);
+ expect(oApi._checkIdsInADS.args[0][0].get('q')).eql(["alternate_bibcode:(\"bibcode-foo\" OR \"test-bibcode\")"]);
+ expect(oApi._checkIdsInADS.args[1][0].get('q')).eql(["bibcode:(\"bibcode-foo\" OR \"test-bibcode\")"]);
+
+
+ expect(recInfo.isCreatedByADS).to.eql(true);
+ expect(recInfo.isCreatedByOthers).to.eql(false);
+
+ // this one should return immediately
+ oApi.getRecordInfo({bibcode: 'bibcode-foo'})
+ .done(function(recInfo) {
+ expect(recInfo.isCreatedByADS).to.eql(false);
+ expect(recInfo.isCreatedByOthers).to.eql(true);
+ expect(recInfo.isKnownToAds).to.eql(true);
+ });
+
+ oApi.getRecordInfo({doi: '10.1126/science.276.5309.88'}) // doi of bibcode-foo
+ .done(function(recInfo) {
+ expect(recInfo.isCreatedByADS).to.eql(false);
+ expect(recInfo.isCreatedByOthers).to.eql(true);
+ expect(recInfo.isKnownToAds).to.eql(true);
+ });
+
+ oApi.getRecordInfo({doi: '10.1103/physrevlett.84.3823'}) // test-bibcode
+ .done(function(recInfo) {
+ expect(recInfo.isCreatedByADS).to.eql(true);
+ expect(recInfo.isCreatedByOthers).to.eql(false);
+ expect(recInfo.isKnownToAds).to.eql(true);
+ });
+
+ oApi.getRecordInfo({bibcode: '1997Sci...276...88V'}) // alternate bibcode of bibcode-foo
+ .done(function(recInfo) {
+ expect(recInfo.isCreatedByADS).to.eql(false);
+ expect(recInfo.isCreatedByOthers).to.eql(true);
+ expect(recInfo.isKnownToAds).to.eql(true);
+ });
+
+ // found by one of the queries, but could not be mapped to bibcode
+ // this should not normally be happening, but i've added the logic
+ // to accomodate it - just in case...
+ oApi.getRecordInfo({bibcode: '2015CeMDA.tmp....1D'})
+ .done(function(recInfo) {
+ expect(recInfo.isCreatedByADS).to.eql(false);
+ expect(recInfo.isCreatedByOthers).to.eql(true);
+ expect(recInfo.isKnownToAds).to.eql(false);
+ });
+
+ // non-ADS record
+ oApi.getRecordInfo({bibcode: 'sfasdfsdfsdfsdfsdf'})
+ .done(function(recInfo) {
+ expect(recInfo.isCreatedByADS).to.eql(false);
+ expect(recInfo.isCreatedByOthers).to.eql(false);
+ expect(recInfo.isKnownToAds).to.eql(false);
+ });
+
+ oApi._checkIdsInADS.restore();
+ oApi.updateDatabase.restore();
- // for testing purpose, force split into many queries
- oApi.maxQuerySize = 2;
-
- oApi.getRecordInfo({bibcode: '2018cnsns..56..296s'})
- .done(function (recInfo) {
-
- expect(oApi._checkIdsInADS.called).to.eql(true);
- expect(oApi._checkIdsInADS.calledTwice).to.eql(true);
- expect(oApi._checkIdsInADS.args[0][0].get('q')).eql(["alternate_bibcode:(\"bibcode-foo\" OR \"test-bibcode\")"]);
- expect(oApi._checkIdsInADS.args[1][0].get('q')).eql(["bibcode:(\"bibcode-foo\" OR \"test-bibcode\")"]);
-
-
- expect(recInfo.isCreatedByADS).to.eql(true);
- expect(recInfo.isCreatedByOthers).to.eql(false);
-
- // this one should return immediately
- oApi.getRecordInfo({bibcode: 'bibcode-foo'})
- .done(function(recInfo) {
- expect(recInfo.isCreatedByADS).to.eql(false);
- expect(recInfo.isCreatedByOthers).to.eql(true);
- expect(recInfo.isKnownToAds).to.eql(true);
- });
-
- oApi.getRecordInfo({doi: '10.1126/science.276.5309.88'}) // doi of bibcode-foo
- .done(function(recInfo) {
- expect(recInfo.isCreatedByADS).to.eql(false);
- expect(recInfo.isCreatedByOthers).to.eql(true);
- expect(recInfo.isKnownToAds).to.eql(true);
- });
-
- oApi.getRecordInfo({doi: '10.1103/physrevlett.84.3823'}) // test-bibcode
- .done(function(recInfo) {
- expect(recInfo.isCreatedByADS).to.eql(true);
- expect(recInfo.isCreatedByOthers).to.eql(false);
- expect(recInfo.isKnownToAds).to.eql(true);
- });
-
- oApi.getRecordInfo({bibcode: '1997Sci...276...88V'}) // alternate bibcode of bibcode-foo
- .done(function(recInfo) {
- expect(recInfo.isCreatedByADS).to.eql(false);
- expect(recInfo.isCreatedByOthers).to.eql(true);
- expect(recInfo.isKnownToAds).to.eql(true);
- });
-
- // found by one of the queries, but could not be mapped to bibcode
- // this should not normally be happening, but i've added the logic
- // to accomodate it - just in case...
- oApi.getRecordInfo({bibcode: '2015CeMDA.tmp....1D'})
- .done(function(recInfo) {
- expect(recInfo.isCreatedByADS).to.eql(false);
- expect(recInfo.isCreatedByOthers).to.eql(true);
- expect(recInfo.isKnownToAds).to.eql(false);
- });
-
- // non-ADS record
- oApi.getRecordInfo({bibcode: 'sfasdfsdfsdfsdfsdf'})
- .done(function(recInfo) {
- expect(recInfo.isCreatedByADS).to.eql(false);
- expect(recInfo.isCreatedByOthers).to.eql(false);
- expect(recInfo.isKnownToAds).to.eql(false);
- });
-
- oApi._checkIdsInADS.restore();
- oApi.updateDatabase.restore();
-
- done();
- });
+ done();
+ });
- });
});
});
});