diff --git a/services/web/app/coffee/Features/Subscription/SubscriptionLocator.coffee b/services/web/app/coffee/Features/Subscription/SubscriptionLocator.coffee index 33376f504b..ef6693f9b8 100644 --- a/services/web/app/coffee/Features/Subscription/SubscriptionLocator.coffee +++ b/services/web/app/coffee/Features/Subscription/SubscriptionLocator.coffee @@ -32,4 +32,7 @@ module.exports = Subscription.find {member_ids: user_id}, {_id:1, planCode:1}, callback getGroupsWithEmailInvite: (email, callback) -> - Subscription.find { invited_emails: email }, callback \ No newline at end of file + Subscription.find { invited_emails: email }, callback + + getGroupWithV1Id: (v1TeamId, callback) -> + Subscription.findOne { "overleaf.id": v1TeamId }, callback diff --git a/services/web/app/coffee/Features/Subscription/SubscriptionUpdater.coffee b/services/web/app/coffee/Features/Subscription/SubscriptionUpdater.coffee index 16dc5adf25..7867db29b9 100644 --- a/services/web/app/coffee/Features/Subscription/SubscriptionUpdater.coffee +++ b/services/web/app/coffee/Features/Subscription/SubscriptionUpdater.coffee @@ -2,6 +2,7 @@ async = require("async") _ = require("underscore") Subscription = require('../../models/Subscription').Subscription SubscriptionLocator = require("./SubscriptionLocator") +UserGetter = require("../User/UserGetter") PlansLocator = require("./PlansLocator") Settings = require("settings-sharelatex") logger = require("logger-sharelatex") @@ -24,17 +25,26 @@ module.exports = SubscriptionUpdater = return callback(err) if err? SubscriptionUpdater._updateSubscriptionFromRecurly recurlySubscription, subscription, callback - addUserToGroup: (adminUser_id, user_id, callback)-> - logger.log adminUser_id:adminUser_id, user_id:user_id, "adding user into mongo subscription" + addUserToGroup: (adminUserId, userId, callback)-> + @addUsersToGroup(adminUserId, [userId], callback) + + addUsersToGroup: (adminUserId, memberIds, callback)-> + logger.log adminUserId: adminUserId, memberIds: memberIds, "adding members into mongo subscription" searchOps = - admin_id: adminUser_id + admin_id: adminUserId insertOperation = - "$addToSet": {member_ids:user_id} - Subscription.findAndModify searchOps, insertOperation, (err, subscription)-> - if err? - logger.err err:err, searchOps:searchOps, insertOperation:insertOperation, "error findy and modify add user to group" - return callback(err) - FeaturesUpdater.refreshFeatures user_id, callback + { $push: { member_ids: { $each: memberIds } } } + + Subscription.findAndModify searchOps, insertOperation, (err, subscription) -> + return callback(err) if err? + + # Only apply features updates to users, not user stubs + UserGetter.getUsers memberIds, { _id: 1 }, (err, users) -> + return callback(err) if err? + + userIds = users.map (u) -> u._id.toString() + async.map userIds, FeaturesUpdater.refreshFeatures, callback + removeUserFromGroup: (adminUser_id, user_id, callback)-> searchOps = @@ -47,6 +57,9 @@ module.exports = SubscriptionUpdater = return callback(err) FeaturesUpdater.refreshFeatures user_id, callback + deleteWithV1Id: (v1TeamId, callback)-> + Subscription.deleteOne { "overleaf.id": v1TeamId }, callback + deleteSubscription: (subscription_id, callback = (error) ->) -> SubscriptionLocator.getSubscription subscription_id, (err, subscription) -> return callback(err) if err? diff --git a/services/web/test/unit/coffee/Subscription/SubscriptionUpdaterTests.coffee b/services/web/test/unit/coffee/Subscription/SubscriptionUpdaterTests.coffee index dd2afad56b..f97ba98ad4 100644 --- a/services/web/test/unit/coffee/Subscription/SubscriptionUpdaterTests.coffee +++ b/services/web/test/unit/coffee/Subscription/SubscriptionUpdaterTests.coffee @@ -4,16 +4,16 @@ expect = require('chai').expect sinon = require 'sinon' modulePath = "../../../../app/js/Features/Subscription/SubscriptionUpdater" assert = require("chai").assert -ObjectId = require('mongoose').Types.ObjectId +ObjectId = require('mongoose').Types.ObjectId describe "SubscriptionUpdater", -> beforeEach -> - @recurlySubscription = + @recurlySubscription = uuid: "1238uoijdasjhd" plan: plan_code: "kjhsakjds" - @adminUser = + @adminUser = _id: @adminuser_id = "5208dd34438843e2db000007" @otherUserId = "5208dd34438842e2db000005" @allUserIds = ["13213", "dsadas", "djsaiud89"] @@ -36,18 +36,18 @@ describe "SubscriptionUpdater", -> @updateStub = sinon.stub().callsArgWith(2, null) @findAndModifyStub = sinon.stub().callsArgWith(2, null, @subscription) @SubscriptionModel = class - constructor: (opts)-> + constructor: (opts)-> subscription.admin_id = opts.admin_id return subscription @remove: sinon.stub().yields() @SubscriptionModel.update = @updateStub @SubscriptionModel.findAndModify = @findAndModifyStub - @SubscriptionLocator = + @SubscriptionLocator = getUsersSubscription: sinon.stub() getGroupSubscriptionMemberOf:sinon.stub() - - @Settings = + + @Settings = freeTrialPlanCode: "collaborator" defaultPlanCode: "personal" defaultFeatures: { "default": "features" } @@ -58,12 +58,18 @@ describe "SubscriptionUpdater", -> @PlansLocator = findLocalPlanInSettings: sinon.stub().returns({}) + @UserGetter = + getUsers: (memberIds, projection, callback) -> + users = memberIds.map (id) -> { _id: id } + callback(null, users) + @ReferalFeatures = getBonusFeatures: sinon.stub().callsArgWith(1) @Modules = {hooks: {fire: sinon.stub().callsArgWith(2, null, null)}} @SubscriptionUpdater = SandboxedModule.require modulePath, requires: '../../models/Subscription': Subscription:@SubscriptionModel './UserFeaturesUpdater': @UserFeaturesUpdater './SubscriptionLocator': @SubscriptionLocator + '../User/UserGetter': @UserGetter './PlansLocator': @PlansLocator "logger-sharelatex": log:-> 'settings-sharelatex': @Settings @@ -73,7 +79,6 @@ describe "SubscriptionUpdater", -> describe "syncSubscription", -> beforeEach -> - @SubscriptionLocator.getUsersSubscription.callsArgWith(1, null, @subscription) @SubscriptionUpdater._updateSubscriptionFromRecurly = sinon.stub().callsArgWith(2) @@ -87,7 +92,6 @@ describe "SubscriptionUpdater", -> done() it "should not call updateFeatures with group subscription if recurly subscription is not expired", (done)-> - @SubscriptionUpdater.syncSubscription @recurlySubscription, @adminUser._id, (err)=> @SubscriptionLocator.getUsersSubscription.calledWith(@adminUser._id).should.equal true @SubscriptionUpdater._updateSubscriptionFromRecurly.called.should.equal true @@ -99,7 +103,7 @@ describe "SubscriptionUpdater", -> describe "_updateSubscriptionFromRecurly", -> beforeEach -> @FeaturesUpdater.refreshFeatures = sinon.stub().callsArgWith(1) - + it "should update the subscription with token etc when not expired", (done)-> @SubscriptionUpdater._updateSubscriptionFromRecurly @recurlySubscription, @subscription, (err)=> @subscription.recurlySubscription_id.should.equal @recurlySubscription.uuid @@ -143,7 +147,6 @@ describe "SubscriptionUpdater", -> done() - describe "_createNewSubscription", -> it "should create a new subscription then update the subscription", (done)-> @SubscriptionUpdater._createNewSubscription @adminUser._id, => @@ -153,15 +156,25 @@ describe "SubscriptionUpdater", -> done() describe "addUserToGroup", -> + beforeEach -> + @SubscriptionUpdater.addUsersToGroup = sinon.stub().yields(null) + + it "delegates to addUsersToGroup", (done)-> + @SubscriptionUpdater.addUserToGroup @adminUser._id, @otherUserId, => + @SubscriptionUpdater.addUsersToGroup + .calledWith(@adminUser._id, [@otherUserId]).should.equal true + done() + + describe "addUsersToGroup", -> beforeEach -> @FeaturesUpdater.refreshFeatures = sinon.stub().callsArgWith(1) - it "should add the users id to the group as a set", (done)-> - @SubscriptionUpdater.addUserToGroup @adminUser._id, @otherUserId, => - searchOps = + it "should add the user ids to the group as a set", (done)-> + @SubscriptionUpdater.addUsersToGroup @adminUser._id, [@otherUserId], => + searchOps = admin_id: @adminUser._id - insertOperation = - "$addToSet": {member_ids:@otherUserId} + insertOperation = + { $push: { member_ids: { $each: [@otherUserId] } } } @findAndModifyStub.calledWith(searchOps, insertOperation).should.equal true done() @@ -176,9 +189,9 @@ describe "SubscriptionUpdater", -> it "should pull the users id from the group", (done)-> @SubscriptionUpdater.removeUserFromGroup @adminUser._id, @otherUserId, => - searchOps = + searchOps = admin_id:@adminUser._id - removeOperation = + removeOperation = "$pull": {member_ids:@otherUserId} @updateStub.calledWith(searchOps, removeOperation).should.equal true done() @@ -199,22 +212,22 @@ describe "SubscriptionUpdater", -> @SubscriptionLocator.getSubscription = sinon.stub().yields(null, @subscription) @FeaturesUpdater.refreshFeatures = sinon.stub().yields() @SubscriptionUpdater.deleteSubscription @subscription_id, done - + it "should look up the subscription", -> @SubscriptionLocator.getSubscription .calledWith(@subscription_id) .should.equal true - + it "should remove the subscription", -> @SubscriptionModel.remove .calledWith({_id: ObjectId(@subscription_id)}) .should.equal true - + it "should downgrade the admin_id", -> @FeaturesUpdater.refreshFeatures .calledWith(@subscription.admin_id) .should.equal true - + it "should downgrade all of the members", -> for user_id in @subscription.member_ids @FeaturesUpdater.refreshFeatures