فهرست منبع

Merge pull request #29838 from overleaf/rh-cio-remove-limits

Enable customer.io for all users

GitOrigin-RevId: 74d8424924ec30ad3a6cb48c15e285cc696186fd
roo hutton 6 ماه پیش
والد
کامیت
77c66132b0

+ 7 - 10
services/web/app/src/Features/Project/ProjectController.mjs

@@ -807,6 +807,12 @@ const _ProjectController = {
         userIsMemberOfGroupSubscription
         userIsMemberOfGroupSubscription
       )
       )
 
 
+      AnalyticsManager.setUserPropertyForUserInBackground(
+        userId,
+        'customer-io-integration',
+        true
+      )
+
       const template =
       const template =
         detachRole === 'detached'
         detachRole === 'detached'
           ? 'project/ide-react-detached'
           ? 'project/ide-react-detached'
@@ -845,15 +851,6 @@ const _ProjectController = {
           fullFeatureSet?.aiErrorAssistant) &&
           fullFeatureSet?.aiErrorAssistant) &&
         !assistantDisabled
         !assistantDisabled
 
 
-      const customerIoEnabled =
-        await SplitTestHandler.promises.hasUserBeenAssignedToVariant(
-          req,
-          userId,
-          'customer-io-trial-conversion',
-          'enabled',
-          true
-        )
-
       const addonPrices =
       const addonPrices =
         isOverleafAssistBundleEnabled &&
         isOverleafAssistBundleEnabled &&
         (await ProjectController._getAddonPrices(req, res))
         (await ProjectController._getAddonPrices(req, res))
@@ -971,7 +968,7 @@ const _ProjectController = {
         isSaas: Features.hasFeature('saas'),
         isSaas: Features.hasFeature('saas'),
         shouldLoadHotjar,
         shouldLoadHotjar,
         isOverleafAssistBundleEnabled,
         isOverleafAssistBundleEnabled,
-        customerIoEnabled,
+        customerIoEnabled: true,
         addonPrices,
         addonPrices,
         compileSettings: {
         compileSettings: {
           compileTimeout: ownerFeatures?.compileTimeout,
           compileTimeout: ownerFeatures?.compileTimeout,

+ 31 - 55
services/web/app/src/Features/Project/ProjectListController.mjs

@@ -179,6 +179,13 @@ async function projectListPage(req, res, next) {
 
 
   user.refProviders = _.mapValues(user.refProviders, Boolean)
   user.refProviders = _.mapValues(user.refProviders, Boolean)
 
 
+  let onboardingDataCollection
+  let customerIoEnabled = false
+  let subjectArea
+  let usedLatex
+  let primaryOccupation
+  let role
+
   if (isSaas) {
   if (isSaas) {
     await SplitTestSessionHandler.promises.sessionMaintenance(req, user)
     await SplitTestSessionHandler.promises.sessionMaintenance(req, user)
 
 
@@ -227,6 +234,26 @@ async function projectListPage(req, res, next) {
     ) {
     ) {
       return res.redirect('/user/emails/primary-email-check')
       return res.redirect('/user/emails/primary-email-check')
     }
     }
+
+    onboardingDataCollection = await OnboardingDataCollection.findById(
+      userId,
+      'subjectArea usedLatex primaryOccupation role'
+    )
+
+    if (onboardingDataCollection) {
+      subjectArea = onboardingDataCollection.subjectArea
+      usedLatex = onboardingDataCollection.usedLatex
+      primaryOccupation = onboardingDataCollection.primaryOccupation
+      role = onboardingDataCollection.role
+    }
+
+    customerIoEnabled = true
+
+    AnalyticsManager.setUserPropertyForUserInBackground(
+      userId,
+      'customer-io-integration',
+      true
+    )
   }
   }
 
 
   const tags = await TagsHandler.promises.getAllTags(userId)
   const tags = await TagsHandler.promises.getAllTags(userId)
@@ -274,6 +301,10 @@ async function projectListPage(req, res, next) {
       return result
       return result
     })
     })
 
 
+  const commonsInstitution = userAffiliations.find(
+    affiliation => affiliation.institution?.commonsAccount
+  )?.institution?.name
+
   const portalTemplates = _buildPortalTemplatesList(userAffiliations)
   const portalTemplates = _buildPortalTemplatesList(userAffiliations)
 
 
   const { allInReconfirmNotificationPeriods } = userEmailsData
   const { allInReconfirmNotificationPeriods } = userEmailsData
@@ -474,64 +505,9 @@ async function projectListPage(req, res, next) {
     logger.error({ err: error }, 'Failed to get individual subscription')
     logger.error({ err: error }, 'Failed to get individual subscription')
   }
   }
 
 
-  const affiliations = userAffiliations || []
-  const commonsInstitution = affiliations.find(
-    affiliation => affiliation.institution?.commonsAccount
-  )?.institution?.name
-
-  let onboardingDataCollection
-  let subjectArea
-  let usedLatex
-  let primaryOccupation
-  let role
-
-  // customer.io: Premium nudge experiment
-  // Only do customer-io-trial-conversion assignment for users not in India/China and not in group/commons
-  let customerIoEnabled = false
   const aiBlocked = !(await _canUseAIAssist(user))
   const aiBlocked = !(await _canUseAIAssist(user))
   const hasAiAssist = await _userHasAIAssist(user)
   const hasAiAssist = await _userHasAIAssist(user)
 
 
-  if (!userIsMemberOfGroupSubscription && !commonsInstitution && isSaas) {
-    try {
-      const excludedCountries = ['IN', 'CN']
-
-      if (!excludedCountries.includes(countryCode)) {
-        const cioAssignment =
-          await SplitTestHandler.promises.getAssignmentForUser(
-            userId,
-            'customer-io-trial-conversion'
-          )
-        if (cioAssignment.variant === 'enabled') {
-          customerIoEnabled = true
-          onboardingDataCollection = await OnboardingDataCollection.findById(
-            userId,
-            'subjectArea usedLatex primaryOccupation role'
-          )
-
-          if (onboardingDataCollection) {
-            subjectArea = onboardingDataCollection.subjectArea
-            usedLatex = onboardingDataCollection.usedLatex
-            primaryOccupation = onboardingDataCollection.primaryOccupation
-            role = onboardingDataCollection.role
-          }
-
-          AnalyticsManager.setUserPropertyForUserInBackground(
-            userId,
-            'customer-io-integration',
-            true
-          )
-        }
-      }
-    } catch (err) {
-      logger.error(
-        { err },
-        'Error checking geo location for customer-io-trial-conversion'
-      )
-      // Fallback to not enabled if geoip fails
-      customerIoEnabled = false
-    }
-  }
-
   await SplitTestHandler.promises.getAssignment(
   await SplitTestHandler.promises.getAssignment(
     req,
     req,
     res,
     res,

+ 2 - 22
services/web/app/src/Features/Subscription/RecurlyEventHandler.mjs

@@ -1,4 +1,3 @@
-import SplitTestHandler from '../SplitTests/SplitTestHandler.mjs'
 import AnalyticsManager from '../Analytics/AnalyticsManager.mjs'
 import AnalyticsManager from '../Analytics/AnalyticsManager.mjs'
 import SubscriptionEmailHandler from './SubscriptionEmailHandler.mjs'
 import SubscriptionEmailHandler from './SubscriptionEmailHandler.mjs'
 import { AI_ADD_ON_CODE } from './AiHelper.mjs'
 import { AI_ADD_ON_CODE } from './AiHelper.mjs'
@@ -15,6 +14,8 @@ async function sendRecurlyAnalyticsEvent(event, eventData) {
     return
     return
   }
   }
 
 
+  eventData['customerio-integration'] = true
+
   const subscription =
   const subscription =
     await SubscriptionLocator.promises.getUsersSubscription(userId)
     await SubscriptionLocator.promises.getUsersSubscription(userId)
 
 
@@ -26,16 +27,6 @@ async function sendRecurlyAnalyticsEvent(event, eventData) {
     return
     return
   }
   }
 
 
-  const customerIoEnabled =
-    await SplitTestHandler.promises.hasUserBeenAssignedToVariant(
-      {},
-      userId,
-      'customer-io-trial-conversion',
-      'enabled',
-      true
-    )
-  eventData['customerio-integration'] = customerIoEnabled || false
-
   switch (event) {
   switch (event) {
     case 'new_subscription_notification':
     case 'new_subscription_notification':
       await _sendSubscriptionStartedEvent(userId, eventData)
       await _sendSubscriptionStartedEvent(userId, eventData)
@@ -158,17 +149,6 @@ async function _sendSubscriptionStartedEvent(userId, eventData) {
 
 
   if (isTrial) {
   if (isTrial) {
     await SubscriptionEmailHandler.sendTrialOnboardingEmail(userId, planCode)
     await SubscriptionEmailHandler.sendTrialOnboardingEmail(userId, planCode)
-    const cioAssignment = await SplitTestHandler.promises.getAssignmentForUser(
-      userId,
-      'customer-io-trial-conversion'
-    )
-    if (cioAssignment.variant === 'enabled') {
-      AnalyticsManager.setUserPropertyForUserInBackground(
-        userId,
-        'customer-io-integration',
-        true
-      )
-    }
   }
   }
 }
 }
 
 

+ 1 - 0
services/web/test/unit/src/Project/ProjectController.test.mjs

@@ -417,6 +417,7 @@ describe('ProjectController', function () {
       () => ({
       () => ({
         default: {
         default: {
           recordEventForUserInBackground: () => {},
           recordEventForUserInBackground: () => {},
+          setUserPropertyForUserInBackground: () => {},
         },
         },
       })
       })
     )
     )

+ 5 - 1
services/web/test/unit/src/Project/ProjectListController.test.mjs

@@ -10,7 +10,11 @@ const MODULE_PATH = `${import.meta.dirname}/../../../../app/src/Features/Project
 
 
 // Mock AnalyticsManager as it isn't used in these tests but causes the User model to be imported and redeclares queues
 // Mock AnalyticsManager as it isn't used in these tests but causes the User model to be imported and redeclares queues
 vi.mock('../../../../app/src/Features/Analytics/AnalyticsManager.mjs', () => {
 vi.mock('../../../../app/src/Features/Analytics/AnalyticsManager.mjs', () => {
-  return {}
+  return {
+    default: {
+      setUserPropertyForUserInBackground: () => {},
+    },
+  }
 })
 })
 
 
 describe('ProjectListController', function () {
 describe('ProjectListController', function () {

+ 5 - 89
services/web/test/unit/src/Subscription/RecurlyEventHandler.test.mjs

@@ -120,47 +120,6 @@ describe('RecurlyEventHandler', function () {
   })
   })
 
 
   it('with new_subscription_notification - free trial', async function (ctx) {
   it('with new_subscription_notification - free trial', async function (ctx) {
-    await ctx.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
-      'new_subscription_notification',
-      ctx.eventData
-    )
-    sinon.assert.calledWith(
-      ctx.AnalyticsManager.recordEventForUserInBackground,
-      ctx.userId,
-      'subscription-started',
-      {
-        plan_code: ctx.planCode,
-        quantity: 1,
-        is_trial: true,
-        has_ai_add_on: false,
-        subscriptionId: ctx.eventData.subscription.uuid,
-        payment_provider: 'recurly',
-        'customerio-integration': false,
-      }
-    )
-    sinon.assert.calledWith(
-      ctx.AnalyticsManager.setUserPropertyForUserInBackground,
-      ctx.userId,
-      'subscription-plan-code',
-      ctx.planCode
-    )
-    sinon.assert.calledWith(
-      ctx.AnalyticsManager.setUserPropertyForUserInBackground,
-      ctx.userId,
-      'subscription-state',
-      'active'
-    )
-    sinon.assert.calledWith(
-      ctx.AnalyticsManager.setUserPropertyForUserInBackground,
-      ctx.userId,
-      'subscription-is-trial',
-      true
-    )
-  })
-
-  it('with new_subscription_notification - free trial with customerio integration enabled', async function (ctx) {
-    ctx.SplitTestHandler.promises.hasUserBeenAssignedToVariant.resolves(true)
-
     await ctx.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
     await ctx.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
       'new_subscription_notification',
       'new_subscription_notification',
       ctx.eventData
       ctx.eventData
@@ -232,7 +191,7 @@ describe('RecurlyEventHandler', function () {
         has_ai_add_on: false,
         has_ai_add_on: false,
         subscriptionId: ctx.eventData.subscription.uuid,
         subscriptionId: ctx.eventData.subscription.uuid,
         payment_provider: 'recurly',
         payment_provider: 'recurly',
-        'customerio-integration': false,
+        'customerio-integration': true,
       }
       }
     )
     )
     sinon.assert.calledWith(
     sinon.assert.calledWith(
@@ -252,49 +211,6 @@ describe('RecurlyEventHandler', function () {
   it('with updated_subscription_notification', async function (ctx) {
   it('with updated_subscription_notification', async function (ctx) {
     ctx.planCode = 'new-plan-code'
     ctx.planCode = 'new-plan-code'
     ctx.eventData.subscription.plan.plan_code = ctx.planCode
     ctx.eventData.subscription.plan.plan_code = ctx.planCode
-    await ctx.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
-      'updated_subscription_notification',
-      ctx.eventData
-    )
-    sinon.assert.calledWith(
-      ctx.AnalyticsManager.recordEventForUserInBackground,
-      ctx.userId,
-      'subscription-updated',
-      {
-        plan_code: ctx.planCode,
-        quantity: 1,
-        is_trial: true,
-        has_ai_add_on: false,
-        subscriptionId: ctx.eventData.subscription.uuid,
-        payment_provider: 'recurly',
-        'customerio-integration': false,
-      }
-    )
-    sinon.assert.calledWith(
-      ctx.AnalyticsManager.setUserPropertyForUserInBackground,
-      ctx.userId,
-      'subscription-plan-code',
-      ctx.planCode
-    )
-    sinon.assert.calledWith(
-      ctx.AnalyticsManager.setUserPropertyForUserInBackground,
-      ctx.userId,
-      'subscription-state',
-      'active'
-    )
-    sinon.assert.calledWith(
-      ctx.AnalyticsManager.setUserPropertyForUserInBackground,
-      ctx.userId,
-      'subscription-is-trial',
-      true
-    )
-  })
-
-  it('with updated_subscription_notification with customerio integration enabled', async function (ctx) {
-    ctx.SplitTestHandler.promises.hasUserBeenAssignedToVariant.resolves(true)
-    ctx.planCode = 'new-plan-code'
-    ctx.eventData.subscription.plan.plan_code = ctx.planCode
-
     await ctx.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
     await ctx.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
       'updated_subscription_notification',
       'updated_subscription_notification',
       ctx.eventData
       ctx.eventData
@@ -350,7 +266,7 @@ describe('RecurlyEventHandler', function () {
         has_ai_add_on: false,
         has_ai_add_on: false,
         subscriptionId: ctx.eventData.subscription.uuid,
         subscriptionId: ctx.eventData.subscription.uuid,
         payment_provider: 'recurly',
         payment_provider: 'recurly',
-        'customerio-integration': false,
+        'customerio-integration': true,
       }
       }
     )
     )
     sinon.assert.calledWith(
     sinon.assert.calledWith(
@@ -384,7 +300,7 @@ describe('RecurlyEventHandler', function () {
         has_ai_add_on: false,
         has_ai_add_on: false,
         subscriptionId: ctx.eventData.subscription.uuid,
         subscriptionId: ctx.eventData.subscription.uuid,
         payment_provider: 'recurly',
         payment_provider: 'recurly',
-        'customerio-integration': false,
+        'customerio-integration': true,
       }
       }
     )
     )
     sinon.assert.calledWith(
     sinon.assert.calledWith(
@@ -423,7 +339,7 @@ describe('RecurlyEventHandler', function () {
         has_ai_add_on: false,
         has_ai_add_on: false,
         subscriptionId: ctx.eventData.subscription.uuid,
         subscriptionId: ctx.eventData.subscription.uuid,
         payment_provider: 'recurly',
         payment_provider: 'recurly',
-        'customerio-integration': false,
+        'customerio-integration': true,
       }
       }
     )
     )
   })
   })
@@ -443,7 +359,7 @@ describe('RecurlyEventHandler', function () {
         has_ai_add_on: false,
         has_ai_add_on: false,
         subscriptionId: ctx.eventData.subscription.uuid,
         subscriptionId: ctx.eventData.subscription.uuid,
         payment_provider: 'recurly',
         payment_provider: 'recurly',
-        'customerio-integration': false,
+        'customerio-integration': true,
       }
       }
     )
     )
   })
   })