mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-28 23:03:35 -05:00
Merge pull request #7806 from overleaf/ab-group-memberships-bigquery
[web] [analytics] Send group memberships information to analytics & import baseline data GitOrigin-RevId: a15c3131f7b55225ae3949933578475668a195a0
This commit is contained in:
parent
fe18fbe157
commit
5c00fe09e4
2 changed files with 179 additions and 12 deletions
|
@ -64,6 +64,11 @@ async function addUserToGroup(subscriptionId, userId) {
|
||||||
).exec()
|
).exec()
|
||||||
await FeaturesUpdater.promises.refreshFeatures(userId, 'add-to-group')
|
await FeaturesUpdater.promises.refreshFeatures(userId, 'add-to-group')
|
||||||
await _sendUserGroupPlanCodeUserProperty(userId)
|
await _sendUserGroupPlanCodeUserProperty(userId)
|
||||||
|
await _sendSubscriptionEvent(
|
||||||
|
userId,
|
||||||
|
subscriptionId,
|
||||||
|
'group-subscription-joined'
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function removeUserFromGroup(subscriptionId, userId) {
|
async function removeUserFromGroup(subscriptionId, userId) {
|
||||||
|
@ -76,6 +81,11 @@ async function removeUserFromGroup(subscriptionId, userId) {
|
||||||
'remove-user-from-group'
|
'remove-user-from-group'
|
||||||
)
|
)
|
||||||
await _sendUserGroupPlanCodeUserProperty(userId)
|
await _sendUserGroupPlanCodeUserProperty(userId)
|
||||||
|
await _sendSubscriptionEvent(
|
||||||
|
userId,
|
||||||
|
subscriptionId,
|
||||||
|
'group-subscription-left'
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function removeUserFromAllGroups(userId) {
|
async function removeUserFromAllGroups(userId) {
|
||||||
|
@ -94,6 +104,13 @@ async function removeUserFromAllGroups(userId) {
|
||||||
userId,
|
userId,
|
||||||
'remove-user-from-groups'
|
'remove-user-from-groups'
|
||||||
)
|
)
|
||||||
|
for (const subscriptionId of subscriptionIds) {
|
||||||
|
await _sendSubscriptionEvent(
|
||||||
|
userId,
|
||||||
|
subscriptionId,
|
||||||
|
'group-subscription-left'
|
||||||
|
)
|
||||||
|
}
|
||||||
await _sendUserGroupPlanCodeUserProperty(userId)
|
await _sendUserGroupPlanCodeUserProperty(userId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,10 +122,16 @@ async function deleteSubscription(subscription, deleterData) {
|
||||||
// 1. create deletedSubscription
|
// 1. create deletedSubscription
|
||||||
await createDeletedSubscription(subscription, deleterData)
|
await createDeletedSubscription(subscription, deleterData)
|
||||||
|
|
||||||
// 2. remove subscription
|
// 2. notify analytics that members left the subscription
|
||||||
|
await _sendSubscriptionEventForAllMembers(
|
||||||
|
subscription._id,
|
||||||
|
'group-subscription-left'
|
||||||
|
)
|
||||||
|
|
||||||
|
// 3. remove subscription
|
||||||
await Subscription.deleteOne({ _id: subscription._id }).exec()
|
await Subscription.deleteOne({ _id: subscription._id }).exec()
|
||||||
|
|
||||||
// 3. refresh users features
|
// 4. refresh users features
|
||||||
await _scheduleRefreshFeatures(subscription)
|
await _scheduleRefreshFeatures(subscription)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,6 +155,12 @@ async function restoreSubscription(subscriptionId) {
|
||||||
await DeletedSubscription.deleteOne({
|
await DeletedSubscription.deleteOne({
|
||||||
'subscription._id': subscription._id,
|
'subscription._id': subscription._id,
|
||||||
}).exec()
|
}).exec()
|
||||||
|
|
||||||
|
// 4. notify analytics that members rejoined the subscription
|
||||||
|
await _sendSubscriptionEventForAllMembers(
|
||||||
|
subscriptionId,
|
||||||
|
'group-subscription-left'
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function refreshUsersFeatures(subscription) {
|
async function refreshUsersFeatures(subscription) {
|
||||||
|
@ -276,6 +305,43 @@ async function _sendUserGroupPlanCodeUserProperty(userId) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function _sendSubscriptionEvent(userId, subscriptionId, event) {
|
||||||
|
const subscription = await Subscription.findOne(
|
||||||
|
{ _id: subscriptionId },
|
||||||
|
{ recurlySubscription_id: 1, groupPlan: 1 }
|
||||||
|
)
|
||||||
|
if (!subscription || !subscription.groupPlan) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
AnalyticsManager.recordEventForUser(userId, event, {
|
||||||
|
groupId: subscription._id.toString(),
|
||||||
|
subscriptionId: subscription.recurlySubscription_id,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function _sendSubscriptionEventForAllMembers(subscriptionId, event) {
|
||||||
|
const subscription = await Subscription.findOne(
|
||||||
|
{ _id: subscriptionId },
|
||||||
|
{
|
||||||
|
recurlySubscription_id: 1,
|
||||||
|
member_ids: 1,
|
||||||
|
groupPlan: 1,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if (!subscription) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const userIds = (subscription.member_ids || []).filter(Boolean)
|
||||||
|
for (const userId of userIds) {
|
||||||
|
if (userId) {
|
||||||
|
AnalyticsManager.recordEventForUser(userId, event, {
|
||||||
|
groupId: subscription._id.toString(),
|
||||||
|
subscriptionId: subscription.recurlySubscription_id,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
updateAdmin: callbackify(updateAdmin),
|
updateAdmin: callbackify(updateAdmin),
|
||||||
syncSubscription: callbackify(syncSubscription),
|
syncSubscription: callbackify(syncSubscription),
|
||||||
|
|
|
@ -25,6 +25,7 @@ describe('SubscriptionUpdater', function () {
|
||||||
member_ids: [],
|
member_ids: [],
|
||||||
save: sinon.stub().resolves(),
|
save: sinon.stub().resolves(),
|
||||||
planCode: 'student_or_something',
|
planCode: 'student_or_something',
|
||||||
|
recurlySubscription_id: 'abc123def456fab789',
|
||||||
}
|
}
|
||||||
this.user_id = this.adminuser_id
|
this.user_id = this.adminuser_id
|
||||||
|
|
||||||
|
@ -36,6 +37,7 @@ describe('SubscriptionUpdater', function () {
|
||||||
save: sinon.stub().resolves(),
|
save: sinon.stub().resolves(),
|
||||||
groupPlan: true,
|
groupPlan: true,
|
||||||
planCode: 'group_subscription',
|
planCode: 'group_subscription',
|
||||||
|
recurlySubscription_id: '456fab789abc123def',
|
||||||
}
|
}
|
||||||
this.betterGroupSubscription = {
|
this.betterGroupSubscription = {
|
||||||
_id: '999999999999999999999999',
|
_id: '999999999999999999999999',
|
||||||
|
@ -45,6 +47,7 @@ describe('SubscriptionUpdater', function () {
|
||||||
save: sinon.stub().resolves(),
|
save: sinon.stub().resolves(),
|
||||||
groupPlan: true,
|
groupPlan: true,
|
||||||
planCode: 'better_group_subscription',
|
planCode: 'better_group_subscription',
|
||||||
|
recurlySubscription_id: '123def456fab789abc',
|
||||||
}
|
}
|
||||||
|
|
||||||
const subscription = this.subscription
|
const subscription = this.subscription
|
||||||
|
@ -66,6 +69,7 @@ describe('SubscriptionUpdater', function () {
|
||||||
this.SubscriptionModel.updateOne = sinon
|
this.SubscriptionModel.updateOne = sinon
|
||||||
.stub()
|
.stub()
|
||||||
.returns({ exec: sinon.stub().resolves() })
|
.returns({ exec: sinon.stub().resolves() })
|
||||||
|
this.SubscriptionModel.findOne = sinon.stub().resolves()
|
||||||
this.SubscriptionModel.updateMany = sinon
|
this.SubscriptionModel.updateMany = sinon
|
||||||
.stub()
|
.stub()
|
||||||
.returns({ exec: sinon.stub().resolves() })
|
.returns({ exec: sinon.stub().resolves() })
|
||||||
|
@ -135,6 +139,7 @@ describe('SubscriptionUpdater', function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.AnalyticsManager = {
|
this.AnalyticsManager = {
|
||||||
|
recordEventForUser: sinon.stub().resolves(),
|
||||||
setUserPropertyForUser: sinon.stub(),
|
setUserPropertyForUser: sinon.stub(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -378,17 +383,30 @@ describe('SubscriptionUpdater', function () {
|
||||||
|
|
||||||
describe('addUserToGroup', function () {
|
describe('addUserToGroup', function () {
|
||||||
it('should add the user ids to the group as a set', async function () {
|
it('should add the user ids to the group as a set', async function () {
|
||||||
|
this.SubscriptionModel.findOne = sinon
|
||||||
|
.stub()
|
||||||
|
.resolves(this.groupSubscription)
|
||||||
|
|
||||||
await this.SubscriptionUpdater.promises.addUserToGroup(
|
await this.SubscriptionUpdater.promises.addUserToGroup(
|
||||||
this.subscription._id,
|
this.groupSubscription._id,
|
||||||
this.otherUserId
|
this.otherUserId
|
||||||
)
|
)
|
||||||
const searchOps = { _id: this.subscription._id }
|
const searchOps = { _id: this.groupSubscription._id }
|
||||||
const insertOperation = {
|
const insertOperation = {
|
||||||
$addToSet: { member_ids: this.otherUserId },
|
$addToSet: { member_ids: this.otherUserId },
|
||||||
}
|
}
|
||||||
this.SubscriptionModel.updateOne
|
this.SubscriptionModel.updateOne
|
||||||
.calledWith(searchOps, insertOperation)
|
.calledWith(searchOps, insertOperation)
|
||||||
.should.equal(true)
|
.should.equal(true)
|
||||||
|
sinon.assert.calledWith(
|
||||||
|
this.AnalyticsManager.recordEventForUser,
|
||||||
|
this.otherUserId,
|
||||||
|
'group-subscription-joined',
|
||||||
|
{
|
||||||
|
groupId: this.groupSubscription._id,
|
||||||
|
subscriptionId: this.groupSubscription.recurlySubscription_id,
|
||||||
|
}
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should update the users features', async function () {
|
it('should update the users features', async function () {
|
||||||
|
@ -450,9 +468,17 @@ describe('SubscriptionUpdater', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('removeUserFromGroups', function () {
|
describe('removeUserFromGroup', function () {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
this.fakeSubscriptions = [{ _id: 'fake-id-1' }, { _id: 'fake-id-2' }]
|
this.fakeSubscriptions = [
|
||||||
|
{
|
||||||
|
_id: 'fake-id-1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_id: 'fake-id-2',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
this.SubscriptionModel.findOne.resolves(this.groupSubscription)
|
||||||
this.SubscriptionLocator.promises.getMemberSubscriptions.resolves(
|
this.SubscriptionLocator.promises.getMemberSubscriptions.resolves(
|
||||||
this.fakeSubscriptions
|
this.fakeSubscriptions
|
||||||
)
|
)
|
||||||
|
@ -469,6 +495,22 @@ describe('SubscriptionUpdater', function () {
|
||||||
.should.equal(true)
|
.should.equal(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should send a group-subscription-left event', async function () {
|
||||||
|
await this.SubscriptionUpdater.promises.removeUserFromGroup(
|
||||||
|
this.groupSubscription._id,
|
||||||
|
this.otherUserId
|
||||||
|
)
|
||||||
|
sinon.assert.calledWith(
|
||||||
|
this.AnalyticsManager.recordEventForUser,
|
||||||
|
this.otherUserId,
|
||||||
|
'group-subscription-left',
|
||||||
|
{
|
||||||
|
groupId: this.groupSubscription._id,
|
||||||
|
subscriptionId: this.groupSubscription.recurlySubscription_id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
it('should set the group plan code user property when removing user from group', async function () {
|
it('should set the group plan code user property when removing user from group', async function () {
|
||||||
await this.SubscriptionUpdater.promises.removeUserFromGroup(
|
await this.SubscriptionUpdater.promises.removeUserFromGroup(
|
||||||
this.subscription._id,
|
this.subscription._id,
|
||||||
|
@ -482,6 +524,29 @@ describe('SubscriptionUpdater', function () {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should update the users features', async function () {
|
||||||
|
await this.SubscriptionUpdater.promises.removeUserFromGroup(
|
||||||
|
this.subscription._id,
|
||||||
|
this.otherUserId
|
||||||
|
)
|
||||||
|
this.FeaturesUpdater.promises.refreshFeatures
|
||||||
|
.calledWith(this.otherUserId)
|
||||||
|
.should.equal(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('removeUserFromAllGroups', function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
this.SubscriptionLocator.promises.getMemberSubscriptions.resolves([
|
||||||
|
{
|
||||||
|
_id: 'fake-id-1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_id: 'fake-id-2',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
it('should set the group plan code user property when removing user from all groups', async function () {
|
it('should set the group plan code user property when removing user from all groups', async function () {
|
||||||
await this.SubscriptionUpdater.promises.removeUserFromAllGroups(
|
await this.SubscriptionUpdater.promises.removeUserFromAllGroups(
|
||||||
this.otherUserId
|
this.otherUserId
|
||||||
|
@ -507,14 +572,50 @@ describe('SubscriptionUpdater', function () {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should update the users features', async function () {
|
it('should send a group-subscription-left event for each group', async function () {
|
||||||
await this.SubscriptionUpdater.promises.removeUserFromGroup(
|
this.fakeSub1 = {
|
||||||
this.subscription._id,
|
_id: 'fake-id-1',
|
||||||
|
groupPlan: true,
|
||||||
|
recurlySubscription_id: 'fake-sub-1',
|
||||||
|
}
|
||||||
|
this.fakeSub2 = {
|
||||||
|
_id: 'fake-id-2',
|
||||||
|
groupPlan: true,
|
||||||
|
recurlySubscription_id: 'fake-sub-2',
|
||||||
|
}
|
||||||
|
this.SubscriptionModel.findOne
|
||||||
|
.withArgs(
|
||||||
|
{ _id: 'fake-id-1' },
|
||||||
|
{ recurlySubscription_id: 1, groupPlan: 1 }
|
||||||
|
)
|
||||||
|
.resolves(this.fakeSub1)
|
||||||
|
.withArgs(
|
||||||
|
{ _id: 'fake-id-2' },
|
||||||
|
{ recurlySubscription_id: 1, groupPlan: 1 }
|
||||||
|
)
|
||||||
|
.resolves(this.fakeSub2)
|
||||||
|
|
||||||
|
await this.SubscriptionUpdater.promises.removeUserFromAllGroups(
|
||||||
this.otherUserId
|
this.otherUserId
|
||||||
)
|
)
|
||||||
this.FeaturesUpdater.promises.refreshFeatures
|
sinon.assert.calledWith(
|
||||||
.calledWith(this.otherUserId)
|
this.AnalyticsManager.recordEventForUser,
|
||||||
.should.equal(true)
|
this.otherUserId,
|
||||||
|
'group-subscription-left',
|
||||||
|
{
|
||||||
|
groupId: 'fake-id-1',
|
||||||
|
subscriptionId: 'fake-sub-1',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
sinon.assert.calledWith(
|
||||||
|
this.AnalyticsManager.recordEventForUser,
|
||||||
|
this.otherUserId,
|
||||||
|
'group-subscription-left',
|
||||||
|
{
|
||||||
|
groupId: 'fake-id-2',
|
||||||
|
subscriptionId: 'fake-sub-2',
|
||||||
|
}
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue