From fb5cdda9daa65215ef3a8294eba57042bece4a83 Mon Sep 17 00:00:00 2001 From: Abram Booth Date: Mon, 14 Aug 2017 15:00:42 -0400 Subject: [PATCH 01/23] [Feature] Update for reviews (#2) * Update models for reviews API * ReviewLog adapter and serializer * Fix log creator model --- addon/adapters/review-log.js | 6 ++++++ addon/models/preprint-provider.js | 7 +++++++ addon/models/preprint.js | 2 ++ addon/models/review-log.js | 16 ++++++++++++++++ addon/serializers/review-log.js | 4 ++++ app/adapters/review-log.js | 1 + app/models/review-log.js | 1 + app/serializers/review-log.js | 1 + tests/unit/adapters/review-log-test.js | 12 ++++++++++++ tests/unit/models/review-log-test.js | 12 ++++++++++++ tests/unit/serializers/review-log-test.js | 15 +++++++++++++++ 11 files changed, 77 insertions(+) create mode 100644 addon/adapters/review-log.js create mode 100644 addon/models/review-log.js create mode 100644 addon/serializers/review-log.js create mode 100644 app/adapters/review-log.js create mode 100644 app/models/review-log.js create mode 100644 app/serializers/review-log.js create mode 100644 tests/unit/adapters/review-log-test.js create mode 100644 tests/unit/models/review-log-test.js create mode 100644 tests/unit/serializers/review-log-test.js diff --git a/addon/adapters/review-log.js b/addon/adapters/review-log.js new file mode 100644 index 000000000..cd5d5fe18 --- /dev/null +++ b/addon/adapters/review-log.js @@ -0,0 +1,6 @@ +import OsfAdapter from './osf-adapter'; +import config from 'ember-get-config'; + +export default OsfAdapter.extend({ + namespace: `${config.OSF.apiNamespace}/reviews`, +}); diff --git a/addon/models/preprint-provider.js b/addon/models/preprint-provider.js index 537a7c017..202086cd2 100644 --- a/addon/models/preprint-provider.js +++ b/addon/models/preprint-provider.js @@ -15,6 +15,13 @@ export default OsfModel.extend({ additionalProviders: DS.attr(), shareSource: DS.attr('string'), preprintWord: DS.attr('string'), + + // Reviews settings + reviewsWorkflow: DS.attr('string'), + reviewsCommentsPrivate: DS.attr('boolean'), + reviewsCommentsAnonymous: DS.attr('boolean'), + permissions: DS.attr(), + // Relationships taxonomies: DS.hasMany('taxonomy'), highlightedTaxonomies: DS.hasMany('taxonomy'), diff --git a/addon/models/preprint.js b/addon/models/preprint.js index 8c0d2c4cc..d95785c90 100644 --- a/addon/models/preprint.js +++ b/addon/models/preprint.js @@ -27,10 +27,12 @@ export default OsfModel.extend({ isPublished: DS.attr('boolean'), isPreprintOrphan: DS.attr('boolean'), licenseRecord: DS.attr(), + reviewsState: DS.attr('string'), // Relationships node: DS.belongsTo('node', { inverse: null, async: true }), license: DS.belongsTo('license', { inverse: null }), primaryFile: DS.belongsTo('file', { inverse: null }), provider: DS.belongsTo('preprint-provider', { inverse: 'preprints', async: true }), + reviewLogs: DS.hasMany('review-log', { inverse: 'reviewable', async: true }), }); diff --git a/addon/models/review-log.js b/addon/models/review-log.js new file mode 100644 index 000000000..440bb64dc --- /dev/null +++ b/addon/models/review-log.js @@ -0,0 +1,16 @@ +import DS from 'ember-data'; +import OsfModel from './osf-model'; + +export default OsfModel.extend({ + action: DS.attr('string'), + comment: DS.attr('string'), + fromState: DS.attr('string'), + toState: DS.attr('string'), + dateCreated: DS.attr('date'), + dateModified: DS.attr('date'), + + // Relationships + provider: DS.belongsTo('preprint-provider', { inverse: null, async: true }), + reviewable: DS.belongsTo('preprint', { inverse: 'reviewLogs', async: true }), + creator: DS.belongsTo('user', { inverse: null, async: true }), +}); diff --git a/addon/serializers/review-log.js b/addon/serializers/review-log.js new file mode 100644 index 000000000..c5617e1d4 --- /dev/null +++ b/addon/serializers/review-log.js @@ -0,0 +1,4 @@ +import OsfSerializer from './osf-serializer'; + +export default OsfSerializer.extend({ +}); diff --git a/app/adapters/review-log.js b/app/adapters/review-log.js new file mode 100644 index 000000000..30a05a4a7 --- /dev/null +++ b/app/adapters/review-log.js @@ -0,0 +1 @@ +export { default } from 'ember-osf/adapters/review-log'; diff --git a/app/models/review-log.js b/app/models/review-log.js new file mode 100644 index 000000000..6c653a99e --- /dev/null +++ b/app/models/review-log.js @@ -0,0 +1 @@ +export { default } from 'ember-osf/models/review-log'; diff --git a/app/serializers/review-log.js b/app/serializers/review-log.js new file mode 100644 index 000000000..3c00f2b9f --- /dev/null +++ b/app/serializers/review-log.js @@ -0,0 +1 @@ +export { default } from 'ember-osf/serializers/review-log'; diff --git a/tests/unit/adapters/review-log-test.js b/tests/unit/adapters/review-log-test.js new file mode 100644 index 000000000..e14e8207c --- /dev/null +++ b/tests/unit/adapters/review-log-test.js @@ -0,0 +1,12 @@ +import { moduleFor, test } from 'ember-qunit'; + +moduleFor('adapter:review-log', 'Unit | Adapter | review log', { + // Specify the other units that are required for this test. + // needs: ['serializer:foo'] +}); + +// Replace this with your real tests. +test('it exists', function(assert) { + let adapter = this.subject(); + assert.ok(adapter); +}); diff --git a/tests/unit/models/review-log-test.js b/tests/unit/models/review-log-test.js new file mode 100644 index 000000000..e981d1684 --- /dev/null +++ b/tests/unit/models/review-log-test.js @@ -0,0 +1,12 @@ +import { moduleForModel, test } from 'ember-qunit'; + +moduleForModel('review-log', 'Unit | Model | review log', { + // Specify the other units that are required for this test. + needs: [] +}); + +test('it exists', function(assert) { + let model = this.subject(); + // let store = this.store(); + assert.ok(!!model); +}); diff --git a/tests/unit/serializers/review-log-test.js b/tests/unit/serializers/review-log-test.js new file mode 100644 index 000000000..b11bd1728 --- /dev/null +++ b/tests/unit/serializers/review-log-test.js @@ -0,0 +1,15 @@ +import { moduleForModel, test } from 'ember-qunit'; + +moduleForModel('review-log', 'Unit | Serializer | review log', { + // Specify the other units that are required for this test. + needs: ['serializer:review-log'] +}); + +// Replace this with your real tests. +test('it serializes records', function(assert) { + let record = this.subject(); + + let serializedRecord = record.serialize(); + + assert.ok(serializedRecord); +}); From c237ed7ad0342658662c808eee754d7a8de680e8 Mon Sep 17 00:00:00 2001 From: Abram Booth Date: Fri, 18 Aug 2017 15:16:31 -0400 Subject: [PATCH 02/23] [Feature] Include reviewable when creating review logs (#3) * Include reviewable when creating review logs * Add title/contributors fields to preprint model * Clean up serializing relationships --- addon/models/preprint.js | 1 + addon/serializers/node.js | 26 ++++-------------------- addon/serializers/osf-serializer.js | 19 ++++++++++++++++-- addon/serializers/preprint.js | 31 +++++++++-------------------- addon/serializers/review-log.js | 5 +++++ 5 files changed, 36 insertions(+), 46 deletions(-) diff --git a/addon/models/preprint.js b/addon/models/preprint.js index d95785c90..b0cd7366e 100644 --- a/addon/models/preprint.js +++ b/addon/models/preprint.js @@ -35,4 +35,5 @@ export default OsfModel.extend({ primaryFile: DS.belongsTo('file', { inverse: null }), provider: DS.belongsTo('preprint-provider', { inverse: 'preprints', async: true }), reviewLogs: DS.hasMany('review-log', { inverse: 'reviewable', async: true }), + contributors: DS.hasMany('contributors', { async: true }), }); diff --git a/addon/serializers/node.js b/addon/serializers/node.js index 113f0e2a3..b0c767bf5 100644 --- a/addon/serializers/node.js +++ b/addon/serializers/node.js @@ -2,26 +2,8 @@ import Ember from 'ember'; import OsfSerializer from './osf-serializer'; export default OsfSerializer.extend({ - serialize(snapshot) { - // Normal OSF serializer strips out relationships. We need to add back primaryFile/node/provider for this endpoint - const res = this._super(...arguments); - res.data.relationships = {}; - let hasRelation = false; - for (var rel in snapshot.record._dirtyRelationships) { - let relationship = Ember.String.underscore(rel); - if (relationship.includes('license')) { - res.data.relationships[relationship] = { - data: { - id: snapshot.belongsTo(rel, { id: true }), - type: 'licenses' - } - }; - hasRelation = true; - } - } - if (!hasRelation) { - delete res.data.relationships; - } - return res; - } + // Serialize license relationship + relationshipTypes: { + license: 'licenses', + }, }); diff --git a/addon/serializers/osf-serializer.js b/addon/serializers/osf-serializer.js index bba353fef..7c7587384 100644 --- a/addon/serializers/osf-serializer.js +++ b/addon/serializers/osf-serializer.js @@ -21,6 +21,9 @@ export default DS.JSONAPISerializer.extend({ } }, + // Map from relationship field name to type. Override to serialize relationships. + relationshipTypes: {}, + /** * Extract information about records embedded inside this request * @method _extractEmbeds @@ -99,8 +102,20 @@ export default DS.JSONAPISerializer.extend({ } } } - // Don't send relationships to the server; this can lead to 500 errors. - delete serialized.data.relationships; + + // Only serialize dirty, whitelisted relationships + serialized.data.relationships = {}; + for (const relationship in snapshot.record._dirtyRelationships) { + const type = this.get('relationshipTypes')[relationship]; + if (type) { + serialized.data.relationships[Ember.String.underscore(relationship)] = { + data: { + id: snapshot.belongsTo(relationship, { id: true }), + type + } + }; + } + } return serialized; }, diff --git a/addon/serializers/preprint.js b/addon/serializers/preprint.js index 00ea977fe..0659804ee 100644 --- a/addon/serializers/preprint.js +++ b/addon/serializers/preprint.js @@ -2,31 +2,18 @@ import OsfSerializer from './osf-serializer'; import Ember from 'ember'; export default OsfSerializer.extend({ + // Serialize these relationships + relationshipTypes: { + primaryFile: 'files', + node: 'nodes', + provider: 'providers', + license: 'licenses', + }, + serialize(snapshot) { - // Normal OSF serializer strips out relationships. We need to add back primaryFile/node/provider for this endpoint const res = this._super(...arguments); - res.data.relationships = {}; - for (var rel in snapshot.record._dirtyRelationships) { - let relationship = Ember.String.underscore(rel); - res.data.relationships[relationship] = { - data: { - id: snapshot.belongsTo(rel, { id: true }), - type: relTypes[rel] - } - }; - } - if (res.data.attributes && 'subjects' in snapshot.record.changedAttributes()) res.data.attributes.subjects = (snapshot.record.get('subjects') || []); return res; - } - + }, }); - -// Type mapping for preprint relationship fields -const relTypes = { - primaryFile: 'files', - node: 'nodes', - provider: 'providers', - license: 'licenses', -}; diff --git a/addon/serializers/review-log.js b/addon/serializers/review-log.js index c5617e1d4..2db1b2a06 100644 --- a/addon/serializers/review-log.js +++ b/addon/serializers/review-log.js @@ -1,4 +1,9 @@ import OsfSerializer from './osf-serializer'; +import Ember from 'ember'; export default OsfSerializer.extend({ + // Serialize reviewable relationship + relationshipTypes: { + reviewable: 'preprints', + }, }); From 429ce0e3f7b90153a5253b084ac7a4cfd4fe7f24 Mon Sep 17 00:00:00 2001 From: Abram Booth Date: Fri, 25 Aug 2017 09:47:41 -0400 Subject: [PATCH 03/23] Add dateLastTransitioned to preprint model (#5) --- addon/models/preprint.js | 1 + 1 file changed, 1 insertion(+) diff --git a/addon/models/preprint.js b/addon/models/preprint.js index b0cd7366e..dc96b77bb 100644 --- a/addon/models/preprint.js +++ b/addon/models/preprint.js @@ -28,6 +28,7 @@ export default OsfModel.extend({ isPreprintOrphan: DS.attr('boolean'), licenseRecord: DS.attr(), reviewsState: DS.attr('string'), + dateLastTransitioned: DS.attr('date'), // Relationships node: DS.belongsTo('node', { inverse: null, async: true }), From 55e69ab8d74665320e2657f55655cd5c7cf94d58 Mon Sep 17 00:00:00 2001 From: Chris Seto Date: Wed, 6 Sep 2017 13:12:39 -0400 Subject: [PATCH 04/23] [Fix] Only fetch the logged in user once --- addon/authenticators/osf-cookie.js | 7 +++++-- addon/services/current-user.js | 17 ++++------------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/addon/authenticators/osf-cookie.js b/addon/authenticators/osf-cookie.js index ecacbd242..d447dffde 100644 --- a/addon/authenticators/osf-cookie.js +++ b/addon/authenticators/osf-cookie.js @@ -20,7 +20,8 @@ import { authenticatedAJAX } from 'ember-osf/utils/ajax-helpers'; */ export default Base.extend({ // HACK: Lets us clear session manually, rather than after .invalidate method resolves - session: Ember.inject.service('session'), + store: Ember.inject.service(), + session: Ember.inject.service(), _test() { return authenticatedAJAX({ @@ -31,7 +32,9 @@ export default Base.extend({ xhrFields: { withCredentials: true } - }).then(function(res) { + }).then(res => { + // Push the result into the store for later use by the current-user service + this.get('store').pushPayload(res); return res.data; }); }, diff --git a/addon/services/current-user.js b/addon/services/current-user.js index 2c243e276..e13eeb319 100644 --- a/addon/services/current-user.js +++ b/addon/services/current-user.js @@ -34,18 +34,9 @@ export default Ember.Service.extend({ * @return {Promise} */ load() { - return new Ember.RSVP.Promise((resolve, reject) => { - var currentUserId = this.get('currentUserId'); - if (currentUserId) { - var currentUser = this.get('store').peekRecord('user', currentUserId); - if (currentUser) { - resolve(currentUser); - } else { - this.get('store').findRecord('user', currentUserId).then((user) => resolve(user), reject); - } - } else { - reject(); - } - }); + let userId = this.get('currentUserId'); + let user = this.get('store').peekRecord('user', userId); + if (user) return Ember.RSVP.resolve(user); + return this.get('store').findRecord('user', userId); } }); From d4338923288324157891c9b055e1d52b93bf1a92 Mon Sep 17 00:00:00 2001 From: Chris Seto Date: Thu, 7 Sep 2017 11:50:43 -0400 Subject: [PATCH 05/23] Allow reviews settings to be nullable --- addon/models/preprint-provider.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addon/models/preprint-provider.js b/addon/models/preprint-provider.js index 202086cd2..934c953ae 100644 --- a/addon/models/preprint-provider.js +++ b/addon/models/preprint-provider.js @@ -18,8 +18,8 @@ export default OsfModel.extend({ // Reviews settings reviewsWorkflow: DS.attr('string'), - reviewsCommentsPrivate: DS.attr('boolean'), - reviewsCommentsAnonymous: DS.attr('boolean'), + reviewsCommentsPrivate: DS.attr('boolean', {allowNull: true}), + reviewsCommentsAnonymous: DS.attr('boolean', {allowNull: true}), permissions: DS.attr(), // Relationships From 0cc20ec0deea65a3b398ace0093d229f1f73d0e0 Mon Sep 17 00:00:00 2001 From: Chris Seto Date: Thu, 7 Sep 2017 13:06:26 -0400 Subject: [PATCH 06/23] Fix infinite authentication --- addon/authenticators/osf-cookie.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/addon/authenticators/osf-cookie.js b/addon/authenticators/osf-cookie.js index d447dffde..20b311e66 100644 --- a/addon/authenticators/osf-cookie.js +++ b/addon/authenticators/osf-cookie.js @@ -34,7 +34,9 @@ export default Base.extend({ } }).then(res => { // Push the result into the store for later use by the current-user service - this.get('store').pushPayload(res); + // Note: we have to deepcopy res because pushPayload mutates our data + // and causes an infinite loop because reasons + this.get('store').pushPayload(Ember.copy(res, true)); return res.data; }); }, @@ -67,11 +69,6 @@ export default Base.extend({ * @return {Promise} */ authenticate(code) { - let jqDeferred = this._test(code); - return new Ember.RSVP.Promise((resolve, reject) => { - // TODO: Improve param capture - jqDeferred.done((value) => resolve(value)); - jqDeferred.fail((reason) => reject(reason)); - }); + return this._test(code); } }); From b844a34fb1267aedeac29ae9fa1f98e6df83799e Mon Sep 17 00:00:00 2001 From: Chris Seto Date: Mon, 11 Sep 2017 09:14:00 -0400 Subject: [PATCH 07/23] Add support for using query parameters in findRecord --- addon/adapters/osf-adapter.js | 4 ++-- addon/models/preprint-provider.js | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/addon/adapters/osf-adapter.js b/addon/adapters/osf-adapter.js index 9e3f136fa..739eadb76 100644 --- a/addon/adapters/osf-adapter.js +++ b/addon/adapters/osf-adapter.js @@ -37,13 +37,13 @@ export default DS.JSONAPIAdapter.extend(HasManyQuery.RESTAdapterMixin, GenericDa * * @method buildQuery */ - buildQuery() { + buildQuery(snapshot) { let query = this._super(...arguments); if (query.include) { query.embed = query.include; } delete query.include; - return query; + return Ember.merge(query, Ember.getWithDefault(snapshot, 'adapterOptions.query', {})); }, buildURL(modelName, id, snapshot, requestType) { var url = this._super(...arguments); diff --git a/addon/models/preprint-provider.js b/addon/models/preprint-provider.js index 934c953ae..62f150f6c 100644 --- a/addon/models/preprint-provider.js +++ b/addon/models/preprint-provider.js @@ -1,3 +1,4 @@ +import Ember from 'ember'; import DS from 'ember-data'; import OsfModel from 'ember-osf/models/osf-model'; @@ -17,10 +18,11 @@ export default OsfModel.extend({ preprintWord: DS.attr('string'), // Reviews settings + permissions: DS.attr(), reviewsWorkflow: DS.attr('string'), reviewsCommentsPrivate: DS.attr('boolean', {allowNull: true}), reviewsCommentsAnonymous: DS.attr('boolean', {allowNull: true}), - permissions: DS.attr(), + reviewableStatusCounts: Ember.computed.alias('links.relationships.preprints.links.related.meta'), // Relationships taxonomies: DS.hasMany('taxonomy'), From a7f14a0ccd8558f9cd825a65b7df5457c42c4d5e Mon Sep 17 00:00:00 2001 From: Lauren Barker Date: Mon, 11 Sep 2017 11:39:43 -0400 Subject: [PATCH 08/23] [Fix] Use `getRecords()` instead of `.record` (#8) --- addon/models/node.js | 4 ++-- addon/models/osf-model.js | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/addon/models/node.js b/addon/models/node.js index 3fe8572d0..2d8afa1d8 100644 --- a/addon/models/node.js +++ b/addon/models/node.js @@ -150,13 +150,13 @@ export default OsfModel.extend(FileItemMixin, { var contributors = this.hasMany('contributors').hasManyRelationship; this.set( '_dirtyRelationships.contributors.update', - contributors.members.list.filter(m => !m.record.get('isNew') && Object.keys(m.record.changedAttributes()).length > 0) + contributors.members.list.filter(m => !m.getRecord().get('isNew') && Object.keys(m.getRecord().changedAttributes()).length > 0) ); // Need to included created contributors even in relationship // hasLoaded is false this.set( '_dirtyRelationships.contributors.create', - contributors.members.list.filter(m => m.record.get('isNew')) + contributors.members.list.filter(m => m.getRecord().get('isNew')) ); // Contributors are a 'real' delete, not just a de-reference this.set( diff --git a/addon/models/osf-model.js b/addon/models/osf-model.js index 9bd5d93f8..4a5b36a80 100644 --- a/addon/models/osf-model.js +++ b/addon/models/osf-model.js @@ -57,12 +57,12 @@ export default DS.Model.extend(HasManyQuery.ModelMixin, { var relation = this.resolveRelationship(rel); // TODO(samchrisinger): not sure if hasLoaded is a subset if the hasData state if (relation.hasData && relation.hasLoaded) { - var canonicalIds = relation.canonicalMembers.list.map(member => member.record.get('id')); - var currentIds = relation.members.list.map(member => member.record.get('id')); + var canonicalIds = relation.canonicalMembers.list.map(member => member.getRecord().get('id')); + var currentIds = relation.members.list.map(member => member.getRecord().get('id')); var changes = { - create: relation.members.list.filter(m => m.record.get('isNew')), - add: relation.members.list.filter(m => !m.record.get('isNew') && canonicalIds.indexOf(m.record.get('id')) === -1), - remove: relation.canonicalMembers.list.filter(m => currentIds.indexOf(m.record.get('id')) === -1) + create: relation.members.list.filter(m => m.getRecord().get('isNew')), + add: relation.members.list.filter(m => !m.getRecord().get('isNew') && canonicalIds.indexOf(m.getRecord().get('id')) === -1), + remove: relation.canonicalMembers.list.filter(m => currentIds.indexOf(m.getRecord().get('id')) === -1) }; var other = this.get('_dirtyRelationships.${rel}') || {}; From 607311ec7498f8963293c9f52178d6172cd588ad Mon Sep 17 00:00:00 2001 From: Chris Seto Date: Tue, 12 Sep 2017 14:22:01 -0400 Subject: [PATCH 09/23] Tweaks and updates for reviews --- addon/components/citation-widget/component.js | 2 +- addon/components/new-osf-navbar/component.js | 11 +++++++++-- addon/const/service-links.js | 3 ++- addon/models/preprint.js | 19 +++++++++++++++++++ addon/utils/load-relationship.js | 13 +++++++++++++ 5 files changed, 44 insertions(+), 4 deletions(-) diff --git a/addon/components/citation-widget/component.js b/addon/components/citation-widget/component.js index 2c76c57c1..d3aca84eb 100644 --- a/addon/components/citation-widget/component.js +++ b/addon/components/citation-widget/component.js @@ -35,7 +35,7 @@ export default Ember.Component.extend({ node: null, store: Ember.inject.service(), - didRender() { + didReceiveAttrs() { const node = this.get('node'); if (!node) { diff --git a/addon/components/new-osf-navbar/component.js b/addon/components/new-osf-navbar/component.js index 9b47c7c2b..a1884c88f 100644 --- a/addon/components/new-osf-navbar/component.js +++ b/addon/components/new-osf-navbar/component.js @@ -26,7 +26,13 @@ import AnalyticsMixin from 'ember-osf/mixins/analytics'; export default Ember.Component.extend(hostAppName, AnalyticsMixin, { layout, session: Ember.inject.service(), - osfServices: osfServices, + osfServices: Ember.computed('hostAppName', function() { + if (this.get('hostAppName') !== 'preprints') return osfServices; + return osfServices.concat([{ + name: 'REVIEWS', + url: serviceLinks.reviewsHome, + }]); + }), serviceLinks: serviceLinks, host: config.OSF.url, currentService: Ember.computed('hostAppName', function() { // Pulls current service name from consuming service's config file @@ -39,9 +45,10 @@ export default Ember.Component.extend(hostAppName, AnalyticsMixin, { currentServiceLink: Ember.computed('serviceLinks', 'currentService', function() { const serviceMapping = { HOME: 'osfHome', + MEETINGS: 'meetingsHome', PREPRINTS: 'preprintsHome', REGISTRIES: 'registriesHome', - MEETINGS: 'meetingsHome' + REVIEWS: 'reviewsHome', }; const service = this.get('currentService'); return this.get('serviceLinks')[serviceMapping[service]]; diff --git a/addon/const/service-links.js b/addon/const/service-links.js index be8a3bf93..3d75a83b2 100644 --- a/addon/const/service-links.js +++ b/addon/const/service-links.js @@ -31,7 +31,8 @@ const serviceLinks = { registriesDiscover: `${osfUrl}registries/discover/`, registriesHome: `${osfUrl}registries/`, registriesSupport: 'http://help.osf.io/m/registrations/', - settings: `${osfUrl}settings/` + settings: `${osfUrl}settings/`, + reviewsHome: `${osfUrl}reviews/`, }; diff --git a/addon/models/preprint.js b/addon/models/preprint.js index dc96b77bb..10673f9f4 100644 --- a/addon/models/preprint.js +++ b/addon/models/preprint.js @@ -1,3 +1,4 @@ +import Ember from 'ember'; import DS from 'ember-data'; import OsfModel from './osf-model'; @@ -37,4 +38,22 @@ export default OsfModel.extend({ provider: DS.belongsTo('preprint-provider', { inverse: 'preprints', async: true }), reviewLogs: DS.hasMany('review-log', { inverse: 'reviewable', async: true }), contributors: DS.hasMany('contributors', { async: true }), + + uniqueSubjects: Ember.computed('subjects', function() { + if (!this.get('subjects')) return []; + return this.get('subjects').reduce((acc, val) => acc.concat(val), []).uniqBy('id'); + }), + + doiUrl: Ember.computed('doi', function() { + return `https://dx.doi.org/${this.get('doi')}`; + }), + + licenseText: Ember.computed('license', function() { + const text = this.get('license.text') || ''; + const {year = '', copyright_holders = []} = this.get('licenseRecord'); + + return text + .replace(/({{year}})/g, year) + .replace(/({{copyrightHolders}})/g, copyright_holders.join(', ')); + }), }); diff --git a/addon/utils/load-relationship.js b/addon/utils/load-relationship.js index 529aa0770..92793b34a 100644 --- a/addon/utils/load-relationship.js +++ b/addon/utils/load-relationship.js @@ -1,5 +1,18 @@ import Ember from 'ember'; +export const ArrayPromiseProxy = Ember.ArrayProxy.extend(Ember.PromiseProxyMixin); + +export function loadRelation(model, relationship) { + if (model.get('content') && !model.query) { + // model was loaded via a relationship, the proxy object + // does not properly proxy .query so we unwrap it here + model = model.get('content') + } + let results = Ember.A(); + let promise = loadAll(model, relationship, results).then(() => results); + return ArrayPromiseProxy.create({promise}); +} + export default function loadAll(model, relationship, dest, options = {}) { var page = options.page || 1; var query = { From e416b28c84b034df75ff65abe77a85f7cbd482d8 Mon Sep 17 00:00:00 2001 From: Chris Seto Date: Tue, 12 Sep 2017 14:34:28 -0400 Subject: [PATCH 10/23] Fix typo --- addon/components/new-osf-navbar/component.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/components/new-osf-navbar/component.js b/addon/components/new-osf-navbar/component.js index a1884c88f..00c28aa58 100644 --- a/addon/components/new-osf-navbar/component.js +++ b/addon/components/new-osf-navbar/component.js @@ -27,7 +27,7 @@ export default Ember.Component.extend(hostAppName, AnalyticsMixin, { layout, session: Ember.inject.service(), osfServices: Ember.computed('hostAppName', function() { - if (this.get('hostAppName') !== 'preprints') return osfServices; + if (this.get('hostAppName') !== 'Preprints') return osfServices; return osfServices.concat([{ name: 'REVIEWS', url: serviceLinks.reviewsHome, From 9cc89c5c2fb52a2e84bf106ade52b9d7d7b8e35e Mon Sep 17 00:00:00 2001 From: Abram Booth Date: Thu, 14 Sep 2017 16:08:33 -0400 Subject: [PATCH 11/23] [Feature][MOD-73] Rename log to action (minimal branch) (#2) * Rename `review-log` to `action` * plac8 flake8 * Fix test errors --- addon/adapters/action.js | 3 +++ addon/adapters/review-log.js | 6 ------ addon/models/{review-log.js => action.js} | 4 ++-- addon/models/preprint.js | 2 +- addon/serializers/action.js | 12 ++++++++++++ addon/serializers/node.js | 1 - addon/serializers/osf-serializer.js | 2 ++ addon/serializers/preprint.js | 1 - addon/serializers/review-log.js | 9 --------- app/adapters/action.js | 1 + app/adapters/review-log.js | 1 - app/models/action.js | 1 + app/models/review-log.js | 1 - app/serializers/action.js | 1 + app/serializers/review-log.js | 1 - .../adapters/{review-log-test.js => action-test.js} | 2 +- tests/unit/models/action-test.js | 12 ++++++++++++ tests/unit/models/preprint-test.js | 10 +++++----- tests/unit/models/review-log-test.js | 12 ------------ .../{review-log-test.js => action-test.js} | 4 ++-- 20 files changed, 43 insertions(+), 43 deletions(-) create mode 100644 addon/adapters/action.js delete mode 100644 addon/adapters/review-log.js rename addon/models/{review-log.js => action.js} (79%) create mode 100644 addon/serializers/action.js delete mode 100644 addon/serializers/review-log.js create mode 100644 app/adapters/action.js delete mode 100644 app/adapters/review-log.js create mode 100644 app/models/action.js delete mode 100644 app/models/review-log.js create mode 100644 app/serializers/action.js delete mode 100644 app/serializers/review-log.js rename tests/unit/adapters/{review-log-test.js => action-test.js} (81%) create mode 100644 tests/unit/models/action-test.js delete mode 100644 tests/unit/models/review-log-test.js rename tests/unit/serializers/{review-log-test.js => action-test.js} (76%) diff --git a/addon/adapters/action.js b/addon/adapters/action.js new file mode 100644 index 000000000..f9d075992 --- /dev/null +++ b/addon/adapters/action.js @@ -0,0 +1,3 @@ +import OsfAdapter from './osf-adapter'; + +export default OsfAdapter.extend({}); diff --git a/addon/adapters/review-log.js b/addon/adapters/review-log.js deleted file mode 100644 index cd5d5fe18..000000000 --- a/addon/adapters/review-log.js +++ /dev/null @@ -1,6 +0,0 @@ -import OsfAdapter from './osf-adapter'; -import config from 'ember-get-config'; - -export default OsfAdapter.extend({ - namespace: `${config.OSF.apiNamespace}/reviews`, -}); diff --git a/addon/models/review-log.js b/addon/models/action.js similarity index 79% rename from addon/models/review-log.js rename to addon/models/action.js index 440bb64dc..a61057b9e 100644 --- a/addon/models/review-log.js +++ b/addon/models/action.js @@ -2,7 +2,7 @@ import DS from 'ember-data'; import OsfModel from './osf-model'; export default OsfModel.extend({ - action: DS.attr('string'), + actionTrigger: DS.attr('string'), comment: DS.attr('string'), fromState: DS.attr('string'), toState: DS.attr('string'), @@ -11,6 +11,6 @@ export default OsfModel.extend({ // Relationships provider: DS.belongsTo('preprint-provider', { inverse: null, async: true }), - reviewable: DS.belongsTo('preprint', { inverse: 'reviewLogs', async: true }), + target: DS.belongsTo('preprint', { inverse: 'actions', async: true }), creator: DS.belongsTo('user', { inverse: null, async: true }), }); diff --git a/addon/models/preprint.js b/addon/models/preprint.js index 10673f9f4..5a2a65597 100644 --- a/addon/models/preprint.js +++ b/addon/models/preprint.js @@ -36,7 +36,7 @@ export default OsfModel.extend({ license: DS.belongsTo('license', { inverse: null }), primaryFile: DS.belongsTo('file', { inverse: null }), provider: DS.belongsTo('preprint-provider', { inverse: 'preprints', async: true }), - reviewLogs: DS.hasMany('review-log', { inverse: 'reviewable', async: true }), + actions: DS.hasMany('action', { inverse: 'target', async: true }), contributors: DS.hasMany('contributors', { async: true }), uniqueSubjects: Ember.computed('subjects', function() { diff --git a/addon/serializers/action.js b/addon/serializers/action.js new file mode 100644 index 000000000..c1912d843 --- /dev/null +++ b/addon/serializers/action.js @@ -0,0 +1,12 @@ +import OsfSerializer from './osf-serializer'; + +export default OsfSerializer.extend({ + // Because `trigger` is a private method on DS.Model + attrs: { + actionTrigger: 'trigger', + }, + // Serialize `target` relationship + relationshipTypes: { + target: 'preprints', + }, +}); diff --git a/addon/serializers/node.js b/addon/serializers/node.js index b0c767bf5..dfcfba3a0 100644 --- a/addon/serializers/node.js +++ b/addon/serializers/node.js @@ -1,4 +1,3 @@ -import Ember from 'ember'; import OsfSerializer from './osf-serializer'; export default OsfSerializer.extend({ diff --git a/addon/serializers/osf-serializer.js b/addon/serializers/osf-serializer.js index 7c7587384..e575cf394 100644 --- a/addon/serializers/osf-serializer.js +++ b/addon/serializers/osf-serializer.js @@ -106,6 +106,8 @@ export default DS.JSONAPISerializer.extend({ // Only serialize dirty, whitelisted relationships serialized.data.relationships = {}; for (const relationship in snapshot.record._dirtyRelationships) { + // https://stackoverflow.com/questions/29004314/why-are-object-keys-and-for-in-different + if (!snapshot.record._dirtyRelationships.hasOwnProperty(relationship)) continue; const type = this.get('relationshipTypes')[relationship]; if (type) { serialized.data.relationships[Ember.String.underscore(relationship)] = { diff --git a/addon/serializers/preprint.js b/addon/serializers/preprint.js index 0659804ee..101a999c9 100644 --- a/addon/serializers/preprint.js +++ b/addon/serializers/preprint.js @@ -1,5 +1,4 @@ import OsfSerializer from './osf-serializer'; -import Ember from 'ember'; export default OsfSerializer.extend({ // Serialize these relationships diff --git a/addon/serializers/review-log.js b/addon/serializers/review-log.js deleted file mode 100644 index 2db1b2a06..000000000 --- a/addon/serializers/review-log.js +++ /dev/null @@ -1,9 +0,0 @@ -import OsfSerializer from './osf-serializer'; -import Ember from 'ember'; - -export default OsfSerializer.extend({ - // Serialize reviewable relationship - relationshipTypes: { - reviewable: 'preprints', - }, -}); diff --git a/app/adapters/action.js b/app/adapters/action.js new file mode 100644 index 000000000..ee7a1b714 --- /dev/null +++ b/app/adapters/action.js @@ -0,0 +1 @@ +export { default } from 'ember-osf/adapters/action'; diff --git a/app/adapters/review-log.js b/app/adapters/review-log.js deleted file mode 100644 index 30a05a4a7..000000000 --- a/app/adapters/review-log.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from 'ember-osf/adapters/review-log'; diff --git a/app/models/action.js b/app/models/action.js new file mode 100644 index 000000000..29afbd853 --- /dev/null +++ b/app/models/action.js @@ -0,0 +1 @@ +export { default } from 'ember-osf/models/action'; diff --git a/app/models/review-log.js b/app/models/review-log.js deleted file mode 100644 index 6c653a99e..000000000 --- a/app/models/review-log.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from 'ember-osf/models/review-log'; diff --git a/app/serializers/action.js b/app/serializers/action.js new file mode 100644 index 000000000..0d644d42d --- /dev/null +++ b/app/serializers/action.js @@ -0,0 +1 @@ +export { default } from 'ember-osf/serializers/action'; diff --git a/app/serializers/review-log.js b/app/serializers/review-log.js deleted file mode 100644 index 3c00f2b9f..000000000 --- a/app/serializers/review-log.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from 'ember-osf/serializers/review-log'; diff --git a/tests/unit/adapters/review-log-test.js b/tests/unit/adapters/action-test.js similarity index 81% rename from tests/unit/adapters/review-log-test.js rename to tests/unit/adapters/action-test.js index e14e8207c..bd5b90c1e 100644 --- a/tests/unit/adapters/review-log-test.js +++ b/tests/unit/adapters/action-test.js @@ -1,6 +1,6 @@ import { moduleFor, test } from 'ember-qunit'; -moduleFor('adapter:review-log', 'Unit | Adapter | review log', { +moduleFor('adapter:action', 'Unit | Adapter | action', { // Specify the other units that are required for this test. // needs: ['serializer:foo'] }); diff --git a/tests/unit/models/action-test.js b/tests/unit/models/action-test.js new file mode 100644 index 000000000..6d2e1f14c --- /dev/null +++ b/tests/unit/models/action-test.js @@ -0,0 +1,12 @@ +import { moduleForModel, test } from 'ember-qunit'; + +moduleForModel('action', 'Unit | Model | action', { + // Specify the other units that are required for this test. + needs: ['model:preprint'] +}); + +test('it exists', function(assert) { + let model = this.subject(); + // let store = this.store(); + assert.ok(!!model); +}); diff --git a/tests/unit/models/preprint-test.js b/tests/unit/models/preprint-test.js index 7e3a3a8be..46ba06837 100644 --- a/tests/unit/models/preprint-test.js +++ b/tests/unit/models/preprint-test.js @@ -1,12 +1,12 @@ import { moduleForModel, test } from 'ember-qunit'; moduleForModel('preprint', 'Unit | Model | preprint', { - // Specify the other units that are required for this test. - needs: ['model:contributor', 'model:file', 'model:file-provider', 'model:preprint-provider'] + // Specify the other units that are required for this test. + needs: ['model:contributor', 'model:file', 'model:file-provider', 'model:preprint-provider', 'model:action'] }); test('it exists', function(assert) { - let model = this.subject(); - // let store = this.store(); - assert.ok(!!model); + let model = this.subject(); + // let store = this.store(); + assert.ok(!!model); }); diff --git a/tests/unit/models/review-log-test.js b/tests/unit/models/review-log-test.js deleted file mode 100644 index e981d1684..000000000 --- a/tests/unit/models/review-log-test.js +++ /dev/null @@ -1,12 +0,0 @@ -import { moduleForModel, test } from 'ember-qunit'; - -moduleForModel('review-log', 'Unit | Model | review log', { - // Specify the other units that are required for this test. - needs: [] -}); - -test('it exists', function(assert) { - let model = this.subject(); - // let store = this.store(); - assert.ok(!!model); -}); diff --git a/tests/unit/serializers/review-log-test.js b/tests/unit/serializers/action-test.js similarity index 76% rename from tests/unit/serializers/review-log-test.js rename to tests/unit/serializers/action-test.js index b11bd1728..d4fb4bbcb 100644 --- a/tests/unit/serializers/review-log-test.js +++ b/tests/unit/serializers/action-test.js @@ -1,8 +1,8 @@ import { moduleForModel, test } from 'ember-qunit'; -moduleForModel('review-log', 'Unit | Serializer | review log', { +moduleForModel('action', 'Unit | Serializer | action', { // Specify the other units that are required for this test. - needs: ['serializer:review-log'] + needs: ['serializer:action'] }); // Replace this with your real tests. From 838ad021cac4156556e4ce6c5f97238dd1ced527 Mon Sep 17 00:00:00 2001 From: Chris Seto Date: Tue, 12 Sep 2017 14:22:01 -0400 Subject: [PATCH 12/23] [MOD-72][Feature] Add "My Reviews" for users with view_submissions permission --- addon/authenticators/osf-cookie.js | 6 +- addon/components/new-osf-navbar/component.js | 9 +- addon/components/new-osf-navbar/template.hbs | 3 +- addon/helpers/build-secondary-nav-links.js | 12 +- addon/locales/en/translations.js | 1 + addon/models/user.js | 1 + addon/services/current-user.js | 37 +- addon/utils/load-relationship.js | 5 +- yarn.lock | 1142 ++++++++++++------ 9 files changed, 834 insertions(+), 382 deletions(-) diff --git a/addon/authenticators/osf-cookie.js b/addon/authenticators/osf-cookie.js index 20b311e66..e0a5b9530 100644 --- a/addon/authenticators/osf-cookie.js +++ b/addon/authenticators/osf-cookie.js @@ -69,6 +69,10 @@ export default Base.extend({ * @return {Promise} */ authenticate(code) { - return this._test(code); + // NOTE: Must be wrapped in an RSVP promise + // _test returns a Jquery Promise but authenticate expects an RSVP Promise + return new Ember.RSVP.Promise((resolve, reject) => + this._test(code).then(resolve).fail(reject) + ); } }); diff --git a/addon/components/new-osf-navbar/component.js b/addon/components/new-osf-navbar/component.js index 00c28aa58..d66b46048 100644 --- a/addon/components/new-osf-navbar/component.js +++ b/addon/components/new-osf-navbar/component.js @@ -25,14 +25,9 @@ import AnalyticsMixin from 'ember-osf/mixins/analytics'; */ export default Ember.Component.extend(hostAppName, AnalyticsMixin, { layout, + osfServices, session: Ember.inject.service(), - osfServices: Ember.computed('hostAppName', function() { - if (this.get('hostAppName') !== 'Preprints') return osfServices; - return osfServices.concat([{ - name: 'REVIEWS', - url: serviceLinks.reviewsHome, - }]); - }), + currentUser: Ember.inject.service(), serviceLinks: serviceLinks, host: config.OSF.url, currentService: Ember.computed('hostAppName', function() { // Pulls current service name from consuming service's config file diff --git a/addon/components/new-osf-navbar/template.hbs b/addon/components/new-osf-navbar/template.hbs index 76f689ad4..2d43126c8 100644 --- a/addon/components/new-osf-navbar/template.hbs +++ b/addon/components/new-osf-navbar/template.hbs @@ -36,7 +36,8 @@