mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #737 from sharelatex/ta-affiliation-features
Check Institution Licence on Features Update
This commit is contained in:
commit
467a910d74
8 changed files with 167 additions and 4 deletions
|
@ -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)
|
|
@ -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'
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -56,6 +56,7 @@ module.exports =
|
|||
|
||||
defaultFeatures: features.personal
|
||||
defaultPlanCode: 'personal'
|
||||
institutionPlanCode: 'professional'
|
||||
|
||||
plans: plans = [{
|
||||
planCode: "v1_free"
|
||||
|
|
|
@ -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()
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in a new issue