Просмотр исходного кода

check institution features on features update

Tim Alby 8 лет назад
Родитель
Сommit
791dc0df5b

+ 23 - 0
services/web/app/coffee/Features/Institutions/InstitutionsFeatures.coffee

@@ -0,0 +1,23 @@
+UserGetter = require '../User/UserGetter'
+PlansLocator = require '../Subscription/PlansLocator'
+Settings = require 'settings-sharelatex'
+logger = require 'logger-sharelatex'
+
+module.exports = InstitutionsFeatures =
+	getInstitutionsFeatures: (userId, callback = (error, features) ->) ->
+		InstitutionsFeatures.hasLicence userId, (error, hasLicence) ->
+			return callback error if error?
+			return callback(null, {}) unless hasLicence
+			plan = PlansLocator.findLocalPlanInSettings Settings.institutionPlanCode
+			callback(null, plan?.features or {})
+
+
+	hasLicence: (userId, callback = (error, hasLicence) ->) ->
+		UserGetter.getUserFullEmails userId, (error, emailsData) ->
+			return callback error if error?
+
+			affiliation = emailsData.find (emailData) ->
+				licence = emailData.affiliation?.institution?.licence
+				emailData.confirmedAt? and licence? and licence != 'free'
+
+			callback(null, !!affiliation)

+ 6 - 3
services/web/app/coffee/Features/Subscription/FeaturesUpdater.coffee

@@ -7,6 +7,7 @@ Settings = require("settings-sharelatex")
 logger = require("logger-sharelatex")
 ReferalFeatures = require("../Referal/ReferalFeatures")
 V1SubscriptionManager = require("./V1SubscriptionManager")
+InstitutionsFeatures = require '../Institutions/InstitutionsFeatures'
 
 oneMonthInSeconds = 60 * 60 * 24 * 30
 
@@ -21,9 +22,11 @@ module.exports = FeaturesUpdater =
 				if error?
 					logger.err {err: error, user_id}, "error notifying v1 about updated features"
 
+
 		jobs =
 			individualFeatures: (cb) -> FeaturesUpdater._getIndividualFeatures user_id, cb
 			groupFeatureSets:   (cb) -> FeaturesUpdater._getGroupFeatureSets user_id, cb
+			institutionFeatures:(cb) -> InstitutionsFeatures.getInstitutionsFeatures user_id, cb
 			v1Features:         (cb) -> FeaturesUpdater._getV1Features user_id, cb
 			bonusFeatures:      (cb) -> ReferalFeatures.getBonusFeatures user_id, cb
 		async.series jobs, (err, results)->
@@ -32,9 +35,9 @@ module.exports = FeaturesUpdater =
 					"error getting subscription or group for refreshFeatures"
 				return callback(err)
 
-			{individualFeatures, groupFeatureSets, v1Features, bonusFeatures} = results
-			logger.log {user_id, individualFeatures, groupFeatureSets, v1Features, bonusFeatures}, 'merging user features'
-			featureSets = groupFeatureSets.concat [individualFeatures, v1Features, bonusFeatures]
+			{individualFeatures, groupFeatureSets, institutionFeatures, v1Features, bonusFeatures} = results
+			logger.log {user_id, individualFeatures, groupFeatureSets, institutionFeatures, v1Features, bonusFeatures}, 'merging user features'
+			featureSets = groupFeatureSets.concat [individualFeatures, institutionFeatures, v1Features, bonusFeatures]
 			features = _.reduce(featureSets, FeaturesUpdater._mergeFeatures, Settings.defaultFeatures)
 
 			logger.log {user_id, features}, 'updating user features'

+ 21 - 0
services/web/test/acceptance/coffee/FeatureUpdaterTests.coffee

@@ -83,6 +83,27 @@ describe "FeatureUpdater.refreshFeatures", ->
 				))
 				done()
 
+	describe "when the user has affiliations", ->
+		beforeEach ->
+			@institutionPlan = settings.plans.find (plan) ->
+				plan.planCode == settings.institutionPlanCode
+			@email = @user.emails[0].email
+			affiliationData =
+				email: @email
+				institution: { licence: 'pro_plus' }
+			MockV1Api.setAffiliations [affiliationData]
+
+		it "should not set their features if email is not confirmed", (done) ->
+			syncUserAndGetFeatures @user, (error, features) =>
+				expect(features).to.deep.equal(settings.defaultFeatures)
+				done()
+
+		it "should set their features if email is confirmed", (done) ->
+			@user.confirmEmail @email, (error) =>
+				syncUserAndGetFeatures @user, (error, features) =>
+					expect(features).to.deep.equal(@institutionPlan.features)
+					done()
+
 	describe "when the user is due bonus features and has extra features that no longer apply", ->
 		beforeEach ->
 			User.update {

+ 5 - 1
services/web/test/acceptance/coffee/helpers/MockV1Api.coffee

@@ -26,6 +26,10 @@ module.exports = MockV1Api =
 
 	syncUserFeatures: sinon.stub()
 
+	affiliations: []
+
+	setAffiliations: (affiliations) -> @affiliations = affiliations
+
 	run: () ->
 		app.get "/api/v1/sharelatex/users/:v1_user_id/plan_code", (req, res, next) =>
 			user = @users[req.params.v1_user_id]
@@ -43,7 +47,7 @@ module.exports = MockV1Api =
 			res.json exportId: @exportId
 
 		app.get "/api/v2/users/:userId/affiliations", (req, res, next) =>
-			res.json []
+			res.json @affiliations
 
 		app.post "/api/v2/users/:userId/affiliations", (req, res, next) =>
 			res.sendStatus 201

+ 5 - 0
services/web/test/acceptance/coffee/helpers/User.coffee

@@ -100,6 +100,11 @@ class User
 		@emails.push(email: email, createdAt: new Date())
 		UserUpdater.addEmailAddress @id, email, callback
 
+	confirmEmail: (email, callback = (error) ->) ->
+		for emailData, idx in @emails
+			@emails[idx].confirmedAt = new Date() if emailData.email == email
+		UserUpdater.confirmEmail @id, email, callback
+
 	ensure_admin: (callback = (error) ->) ->
 		db.users.update {_id: ObjectId(@id)}, { $set: { isAdmin: true }}, callback
 

+ 1 - 0
services/web/test/acceptance/config/settings.test.coffee

@@ -56,6 +56,7 @@ module.exports =
 
 	defaultFeatures: features.personal
 	defaultPlanCode: 'personal'
+	institutionPlanCode: 'professional'
 
 	plans: plans = [{
 		planCode: "v1_free"

+ 96 - 0
services/web/test/unit/coffee/Institutions/InstitutionsFeaturesTests.coffee

@@ -0,0 +1,96 @@
+SandboxedModule = require('sandboxed-module')
+assert = require('assert')
+require('chai').should()
+expect = require('chai').expect
+sinon = require('sinon')
+modulePath = require('path').join __dirname, '../../../../app/js/Features/Institutions/InstitutionsFeatures.js'
+
+describe 'InstitutionsFeatures', ->
+
+	beforeEach ->
+		@UserGetter = getUserFullEmails: sinon.stub()
+		@PlansLocator = findLocalPlanInSettings: sinon.stub()
+		@institutionPlanCode = 'institution_plan_code'
+		@InstitutionsFeatures = SandboxedModule.require modulePath, requires:
+			'../User/UserGetter': @UserGetter
+			'../Subscription/PlansLocator': @PlansLocator
+			'settings-sharelatex': institutionPlanCode: @institutionPlanCode
+			'logger-sharelatex':
+				log:->
+				err:->
+
+		@userId = '12345abcde'
+
+	describe "hasLicence", ->
+			it 'should handle error', (done)->
+				@UserGetter.getUserFullEmails.yields(new Error('Nope'))
+				@InstitutionsFeatures.hasLicence @userId, (error, hasLicence) ->
+					expect(error).to.exist
+					done()
+
+			it 'should return false if user has no affiliations', (done) ->
+				@UserGetter.getUserFullEmails.yields(null, [])
+				@InstitutionsFeatures.hasLicence @userId, (error, hasLicence) ->
+					expect(error).to.not.exist
+					expect(hasLicence).to.be.false
+					done()
+
+			it 'should return false if user has no confirmed affiliations', (done) ->
+				affiliations = [
+					{ confirmedAt: null, affiliation: institution: { licence: 'pro_plus' } }
+				]
+				@UserGetter.getUserFullEmails.yields(null, affiliations)
+				@InstitutionsFeatures.hasLicence @userId, (error, hasLicence) ->
+					expect(error).to.not.exist
+					expect(hasLicence).to.be.false
+					done()
+
+			it 'should return false if user has no paid affiliations', (done) ->
+				affiliations = [
+					{ confirmedAt: new Date(), affiliation: institution: { licence: 'free' } }
+				]
+				@UserGetter.getUserFullEmails.yields(null, affiliations)
+				@InstitutionsFeatures.hasLicence @userId, (error, hasLicence) ->
+					expect(error).to.not.exist
+					expect(hasLicence).to.be.false
+					done()
+
+			it 'should return true if user has confirmed paid affiliation', (done)->
+				affiliations = [
+					{ confirmedAt: new Date(), affiliation: institution: { licence: 'pro_plus' } }
+					{ confirmedAt: new Date(), affiliation: institution: { licence: 'free' } }
+					{ confirmedAt: null, affiliation: institution: { licence: 'pro' } }
+					{ confirmedAt: null, affiliation: institution: { licence: null } }
+					{ confirmedAt: new Date(), affiliation: institution: {} }
+				]
+				@UserGetter.getUserFullEmails.yields(null, affiliations)
+				@InstitutionsFeatures.hasLicence @userId, (error, hasLicence) ->
+					expect(error).to.not.exist
+					expect(hasLicence).to.be.true
+					done()
+
+	describe "getInstitutionsFeatures", ->
+			beforeEach ->
+				@InstitutionsFeatures.hasLicence = sinon.stub()
+				@testFeatures = features: { institution: 'all' }
+				@PlansLocator.findLocalPlanInSettings.withArgs(@institutionPlanCode).returns(@testFeatures)
+
+			it 'should handle error', (done)->
+				@InstitutionsFeatures.hasLicence.yields(new Error('Nope'))
+				@InstitutionsFeatures.getInstitutionsFeatures @userId, (error, features) ->
+					expect(error).to.exist
+					done()
+
+			it 'should return no feaures if user has no plan code', (done) ->
+				@InstitutionsFeatures.hasLicence.yields(null, false)
+				@InstitutionsFeatures.getInstitutionsFeatures @userId, (error, features) ->
+					expect(error).to.not.exist
+					expect(features).to.deep.equal {}
+					done()
+
+			it 'should return feaures if user has affiliations plan code', (done) ->
+				@InstitutionsFeatures.hasLicence.yields(null, true)
+				@InstitutionsFeatures.getInstitutionsFeatures @userId, (error, features) =>
+					expect(error).to.not.exist
+					expect(features).to.deep.equal @testFeatures.features
+					done()

+ 10 - 0
services/web/test/unit/coffee/Subscription/FeaturesUpdaterTests.coffee

@@ -19,6 +19,7 @@ describe "FeaturesUpdater", ->
 			'settings-sharelatex': @Settings = {}
 			"../Referal/ReferalFeatures" : @ReferalFeatures = {}
 			"./V1SubscriptionManager": @V1SubscriptionManager = {}
+			'../Institutions/InstitutionsFeatures': @InstitutionsFeatures = {}
 
 	describe "refreshFeatures", ->
 		beforeEach ->
@@ -26,6 +27,7 @@ describe "FeaturesUpdater", ->
 			@UserFeaturesUpdater.updateFeatures = sinon.stub().yields()
 			@FeaturesUpdater._getIndividualFeatures = sinon.stub().yields(null, { 'individual': 'features' })
 			@FeaturesUpdater._getGroupFeatureSets = sinon.stub().yields(null, [{ 'group': 'features' }, { 'group': 'features2' }])
+			@InstitutionsFeatures.getInstitutionsFeatures = sinon.stub().yields(null, { 'institutions': 'features' })
 			@FeaturesUpdater._getV1Features = sinon.stub().yields(null, { 'v1': 'features' })
 			@ReferalFeatures.getBonusFeatures = sinon.stub().yields(null, { 'bonus': 'features' })
 			@FeaturesUpdater._mergeFeatures = sinon.stub().returns({'merged': 'features'})
@@ -45,6 +47,11 @@ describe "FeaturesUpdater", ->
 					.calledWith(@user_id)
 					.should.equal true
 
+			it "should get the institution features", ->
+				@InstitutionsFeatures.getInstitutionsFeatures
+					.calledWith(@user_id)
+					.should.equal true
+
 			it "should get the v1 features", ->
 				@FeaturesUpdater._getV1Features
 					.calledWith(@user_id)
@@ -65,6 +72,9 @@ describe "FeaturesUpdater", ->
 				@FeaturesUpdater._mergeFeatures.calledWith(sinon.match.any, { 'group': 'features' }).should.equal true
 				@FeaturesUpdater._mergeFeatures.calledWith(sinon.match.any, { 'group': 'features2' }).should.equal true
 
+			it "should merge the institutions features", ->
+				@FeaturesUpdater._mergeFeatures.calledWith(sinon.match.any, { 'institutions': 'features' }).should.equal true
+
 			it "should merge the v1 features", ->
 				@FeaturesUpdater._mergeFeatures.calledWith(sinon.match.any, { 'v1': 'features' }).should.equal true