mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #4639 from overleaf/ab-web-mono-analytics-id
Analytics ID support GitOrigin-RevId: 820a6c0f4d19f046f6c791ce4dc64dbc80748924
This commit is contained in:
parent
8ec01f59da
commit
6dd641e0e6
36 changed files with 557 additions and 249 deletions
|
@ -30,10 +30,12 @@ module.exports = {
|
||||||
if (!Features.hasFeature('analytics')) {
|
if (!Features.hasFeature('analytics')) {
|
||||||
return res.sendStatus(202)
|
return res.sendStatus(202)
|
||||||
}
|
}
|
||||||
const userId =
|
|
||||||
SessionManager.getLoggedInUserId(req.session) || req.sessionID
|
|
||||||
delete req.body._csrf
|
delete req.body._csrf
|
||||||
AnalyticsManager.recordEvent(userId, req.params.event, req.body)
|
AnalyticsManager.recordEventForSession(
|
||||||
|
req.session,
|
||||||
|
req.params.event,
|
||||||
|
req.body
|
||||||
|
)
|
||||||
res.sendStatus(202)
|
res.sendStatus(202)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,32 @@
|
||||||
|
const SessionManager = require('../Authentication/SessionManager')
|
||||||
|
const UserAnalyticsIdCache = require('./UserAnalyticsIdCache')
|
||||||
const Settings = require('@overleaf/settings')
|
const Settings = require('@overleaf/settings')
|
||||||
const Metrics = require('../../infrastructure/Metrics')
|
const Metrics = require('../../infrastructure/Metrics')
|
||||||
const Queues = require('../../infrastructure/Queues')
|
const Queues = require('../../infrastructure/Queues')
|
||||||
|
const uuid = require('uuid')
|
||||||
|
const _ = require('lodash')
|
||||||
|
const { expressify } = require('../../util/promises')
|
||||||
|
|
||||||
const analyticsEventsQueue = Queues.getAnalyticsEventsQueue()
|
const analyticsEventsQueue = Queues.getAnalyticsEventsQueue()
|
||||||
const analyticsEditingSessionsQueue = Queues.getAnalyticsEditingSessionsQueue()
|
const analyticsEditingSessionsQueue = Queues.getAnalyticsEditingSessionsQueue()
|
||||||
const analyticsUserPropertiesQueue = Queues.getAnalyticsUserPropertiesQueue()
|
const analyticsUserPropertiesQueue = Queues.getAnalyticsUserPropertiesQueue()
|
||||||
|
|
||||||
function identifyUser(userId, oldUserId) {
|
const ONE_MINUTE_MS = 60 * 1000
|
||||||
if (!userId || !oldUserId) {
|
|
||||||
|
function identifyUser(userId, analyticsId, isNewUser) {
|
||||||
|
if (!userId || !analyticsId) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (isAnalyticsDisabled() || isSmokeTestUser(userId)) {
|
if (_isAnalyticsDisabled() || _isSmokeTestUser(userId)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
Metrics.analyticsQueue.inc({ status: 'adding', event_type: 'identify' })
|
Metrics.analyticsQueue.inc({ status: 'adding', event_type: 'identify' })
|
||||||
analyticsEventsQueue
|
analyticsEventsQueue
|
||||||
.add('identify', { userId, oldUserId })
|
.add(
|
||||||
|
'identify',
|
||||||
|
{ userId, analyticsId, isNewUser },
|
||||||
|
{ delay: ONE_MINUTE_MS }
|
||||||
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
Metrics.analyticsQueue.inc({ status: 'added', event_type: 'identify' })
|
Metrics.analyticsQueue.inc({ status: 'added', event_type: 'identify' })
|
||||||
})
|
})
|
||||||
|
@ -24,29 +35,81 @@ function identifyUser(userId, oldUserId) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function recordEvent(userId, event, segmentation) {
|
async function recordEventForUser(userId, event, segmentation) {
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (isAnalyticsDisabled() || isSmokeTestUser(userId)) {
|
if (_isAnalyticsDisabled() || _isSmokeTestUser(userId)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
Metrics.analyticsQueue.inc({ status: 'adding', event_type: 'event' })
|
const analyticsId = await UserAnalyticsIdCache.get(userId)
|
||||||
analyticsEventsQueue
|
if (analyticsId) {
|
||||||
.add('event', { userId, event, segmentation })
|
_recordEvent({ analyticsId, userId, event, segmentation, isLoggedIn: true })
|
||||||
.then(() => {
|
}
|
||||||
Metrics.analyticsQueue.inc({ status: 'added', event_type: 'event' })
|
}
|
||||||
})
|
|
||||||
.catch(() => {
|
function recordEventForSession(session, event, segmentation) {
|
||||||
Metrics.analyticsQueue.inc({ status: 'error', event_type: 'event' })
|
const { analyticsId, userId } = getIdsFromSession(session)
|
||||||
|
if (!analyticsId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (_isAnalyticsDisabled() || _isSmokeTestUser(userId)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_recordEvent({
|
||||||
|
analyticsId,
|
||||||
|
userId,
|
||||||
|
event,
|
||||||
|
segmentation,
|
||||||
|
isLoggedIn: !!userId,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function setUserPropertyForUser(userId, propertyName, propertyValue) {
|
||||||
|
if (_isAnalyticsDisabled() || _isSmokeTestUser(userId)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_checkPropertyValue(propertyValue)
|
||||||
|
|
||||||
|
const analyticsId = await UserAnalyticsIdCache.get(userId)
|
||||||
|
if (analyticsId) {
|
||||||
|
_setUserProperty({ analyticsId, propertyName, propertyValue })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setUserPropertyForAnalyticsId(
|
||||||
|
analyticsId,
|
||||||
|
propertyName,
|
||||||
|
propertyValue
|
||||||
|
) {
|
||||||
|
if (_isAnalyticsDisabled()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_checkPropertyValue(propertyValue)
|
||||||
|
|
||||||
|
_setUserProperty({ analyticsId, propertyName, propertyValue })
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setUserPropertyForSession(session, propertyName, propertyValue) {
|
||||||
|
const { analyticsId, userId } = getIdsFromSession(session)
|
||||||
|
if (_isAnalyticsDisabled() || _isSmokeTestUser(userId)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_checkPropertyValue(propertyValue)
|
||||||
|
|
||||||
|
if (analyticsId) {
|
||||||
|
_setUserProperty({ analyticsId, propertyName, propertyValue })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function updateEditingSession(userId, projectId, countryCode) {
|
function updateEditingSession(userId, projectId, countryCode) {
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (isAnalyticsDisabled() || isSmokeTestUser(userId)) {
|
if (_isAnalyticsDisabled() || _isSmokeTestUser(userId)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
Metrics.analyticsQueue.inc({
|
Metrics.analyticsQueue.inc({
|
||||||
|
@ -69,26 +132,36 @@ function updateEditingSession(userId, projectId, countryCode) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function setUserProperty(userId, propertyName, propertyValue) {
|
function _recordEvent(
|
||||||
if (!userId) {
|
{ analyticsId, userId, event, segmentation, isLoggedIn },
|
||||||
return
|
{ delay } = {}
|
||||||
}
|
) {
|
||||||
if (isAnalyticsDisabled() || isSmokeTestUser(userId)) {
|
Metrics.analyticsQueue.inc({ status: 'adding', event_type: 'event' })
|
||||||
return
|
analyticsEventsQueue
|
||||||
}
|
.add(
|
||||||
|
'event',
|
||||||
if (propertyValue === undefined) {
|
{ analyticsId, userId, event, segmentation, isLoggedIn },
|
||||||
throw new Error(
|
{ delay }
|
||||||
'propertyValue cannot be undefined, use null to unset a property'
|
|
||||||
)
|
)
|
||||||
}
|
.then(() => {
|
||||||
|
Metrics.analyticsQueue.inc({ status: 'added', event_type: 'event' })
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
Metrics.analyticsQueue.inc({ status: 'error', event_type: 'event' })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function _setUserProperty({ analyticsId, propertyName, propertyValue }) {
|
||||||
Metrics.analyticsQueue.inc({
|
Metrics.analyticsQueue.inc({
|
||||||
status: 'adding',
|
status: 'adding',
|
||||||
event_type: 'user-property',
|
event_type: 'user-property',
|
||||||
})
|
})
|
||||||
analyticsUserPropertiesQueue
|
analyticsUserPropertiesQueue
|
||||||
.add({ userId, propertyName, propertyValue })
|
.add({
|
||||||
|
analyticsId,
|
||||||
|
propertyName,
|
||||||
|
propertyValue,
|
||||||
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
Metrics.analyticsQueue.inc({
|
Metrics.analyticsQueue.inc({
|
||||||
status: 'added',
|
status: 'added',
|
||||||
|
@ -103,18 +176,54 @@ function setUserProperty(userId, propertyName, propertyValue) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function isSmokeTestUser(userId) {
|
function _isSmokeTestUser(userId) {
|
||||||
const smokeTestUserId = Settings.smokeTest && Settings.smokeTest.userId
|
const smokeTestUserId = Settings.smokeTest && Settings.smokeTest.userId
|
||||||
return smokeTestUserId != null && userId.toString() === smokeTestUserId
|
return smokeTestUserId != null && userId.toString() === smokeTestUserId
|
||||||
}
|
}
|
||||||
|
|
||||||
function isAnalyticsDisabled() {
|
function _isAnalyticsDisabled() {
|
||||||
return !(Settings.analytics && Settings.analytics.enabled)
|
return !(Settings.analytics && Settings.analytics.enabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _checkPropertyValue(propertyValue) {
|
||||||
|
if (propertyValue === undefined) {
|
||||||
|
throw new Error(
|
||||||
|
'propertyValue cannot be undefined, use null to unset a property'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getIdsFromSession(session) {
|
||||||
|
const analyticsId = _.get(session, ['analyticsId'])
|
||||||
|
const userId = SessionManager.getLoggedInUserId(session)
|
||||||
|
return { analyticsId, userId }
|
||||||
|
}
|
||||||
|
|
||||||
|
async function analyticsIdMiddleware(req, res, next) {
|
||||||
|
const session = req.session
|
||||||
|
const sessionUser = SessionManager.getSessionUser(session)
|
||||||
|
if (session.analyticsId) {
|
||||||
|
if (sessionUser && session.analyticsId !== sessionUser.analyticsId) {
|
||||||
|
session.analyticsId = sessionUser.analyticsId
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (sessionUser) {
|
||||||
|
session.analyticsId = sessionUser.analyticsId
|
||||||
|
} else {
|
||||||
|
session.analyticsId = uuid.v4()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
next()
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
identifyUser,
|
identifyUser,
|
||||||
recordEvent,
|
recordEventForSession,
|
||||||
|
recordEventForUser,
|
||||||
|
setUserPropertyForUser,
|
||||||
|
setUserPropertyForSession,
|
||||||
|
setUserPropertyForAnalyticsId,
|
||||||
updateEditingSession,
|
updateEditingSession,
|
||||||
setUserProperty,
|
getIdsFromSession,
|
||||||
|
analyticsIdMiddleware: expressify(analyticsIdMiddleware),
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,7 +79,7 @@ function addUserProperties(userId, session) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (session.referal_id) {
|
if (session.referal_id) {
|
||||||
AnalyticsManager.setUserProperty(
|
AnalyticsManager.setUserPropertyForUser(
|
||||||
userId,
|
userId,
|
||||||
`registered-from-bonus-scheme`,
|
`registered-from-bonus-scheme`,
|
||||||
true
|
true
|
||||||
|
@ -87,7 +87,7 @@ function addUserProperties(userId, session) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (session.required_login_for) {
|
if (session.required_login_for) {
|
||||||
AnalyticsManager.setUserProperty(
|
AnalyticsManager.setUserPropertyForUser(
|
||||||
userId,
|
userId,
|
||||||
`registered-from-${session.required_login_for}`,
|
`registered-from-${session.required_login_for}`,
|
||||||
true
|
true
|
||||||
|
@ -96,7 +96,7 @@ function addUserProperties(userId, session) {
|
||||||
|
|
||||||
if (session.inbound) {
|
if (session.inbound) {
|
||||||
if (session.inbound.referrer) {
|
if (session.inbound.referrer) {
|
||||||
AnalyticsManager.setUserProperty(
|
AnalyticsManager.setUserPropertyForUser(
|
||||||
userId,
|
userId,
|
||||||
`registered-from-referrer-${session.inbound.referrer.medium}`,
|
`registered-from-referrer-${session.inbound.referrer.medium}`,
|
||||||
session.inbound.referrer.detail || 'other'
|
session.inbound.referrer.detail || 'other'
|
||||||
|
@ -106,7 +106,7 @@ function addUserProperties(userId, session) {
|
||||||
if (session.inbound.utm) {
|
if (session.inbound.utm) {
|
||||||
for (const utmKey of UTM_KEYS) {
|
for (const utmKey of UTM_KEYS) {
|
||||||
if (session.inbound.utm[utmKey]) {
|
if (session.inbound.utm[utmKey]) {
|
||||||
AnalyticsManager.setUserProperty(
|
AnalyticsManager.setUserPropertyForUser(
|
||||||
userId,
|
userId,
|
||||||
`registered-from-${utmKey.replace('_', '-')}`,
|
`registered-from-${utmKey.replace('_', '-')}`,
|
||||||
session.inbound.utm[utmKey]
|
session.inbound.utm[utmKey]
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
const UserGetter = require('../User/UserGetter')
|
||||||
|
const { CacheLoader } = require('cache-flow')
|
||||||
|
|
||||||
|
class UserAnalyticsIdCache extends CacheLoader {
|
||||||
|
constructor() {
|
||||||
|
super('user-analytics-id', {
|
||||||
|
expirationTime: 60,
|
||||||
|
maxSize: 10000,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async load(userId) {
|
||||||
|
const user = await UserGetter.promises.getUser(userId, { analyticsId: 1 })
|
||||||
|
if (user) {
|
||||||
|
return user.analyticsId || user._id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
keyToString(userId) {
|
||||||
|
if (userId) {
|
||||||
|
return userId.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = new UserAnalyticsIdCache()
|
|
@ -48,6 +48,7 @@ const AuthenticationController = {
|
||||||
ip_address: user._login_req_ip,
|
ip_address: user._login_req_ip,
|
||||||
must_reconfirm: user.must_reconfirm,
|
must_reconfirm: user.must_reconfirm,
|
||||||
v1_id: user.overleaf != null ? user.overleaf.id : undefined,
|
v1_id: user.overleaf != null ? user.overleaf.id : undefined,
|
||||||
|
analyticsId: user.analyticsId || user._id,
|
||||||
}
|
}
|
||||||
callback(null, lightUser)
|
callback(null, lightUser)
|
||||||
},
|
},
|
||||||
|
@ -82,6 +83,9 @@ const AuthenticationController = {
|
||||||
return res.redirect('/login')
|
return res.redirect('/login')
|
||||||
} // OAuth2 'state' mismatch
|
} // OAuth2 'state' mismatch
|
||||||
|
|
||||||
|
const anonymousAnalyticsId = req.session.analyticsId
|
||||||
|
const isNewUser = req.session.justRegistered || false
|
||||||
|
|
||||||
const Modules = require('../../infrastructure/Modules')
|
const Modules = require('../../infrastructure/Modules')
|
||||||
Modules.hooks.fire(
|
Modules.hooks.fire(
|
||||||
'preFinishLogin',
|
'preFinishLogin',
|
||||||
|
@ -106,7 +110,7 @@ const AuthenticationController = {
|
||||||
|
|
||||||
const redir =
|
const redir =
|
||||||
AuthenticationController._getRedirectFromSession(req) || '/project'
|
AuthenticationController._getRedirectFromSession(req) || '/project'
|
||||||
_loginAsyncHandlers(req, user)
|
_loginAsyncHandlers(req, user, anonymousAnalyticsId, isNewUser)
|
||||||
const userId = user._id
|
const userId = user._id
|
||||||
UserAuditLogHandler.addEntry(userId, 'login', userId, req.ip, err => {
|
UserAuditLogHandler.addEntry(userId, 'login', userId, req.ip, err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -486,7 +490,8 @@ function _afterLoginSessionSetup(req, user, callback) {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
function _loginAsyncHandlers(req, user) {
|
|
||||||
|
function _loginAsyncHandlers(req, user, anonymousAnalyticsId, isNewUser) {
|
||||||
UserHandler.setupLoginData(user, err => {
|
UserHandler.setupLoginData(user, err => {
|
||||||
if (err != null) {
|
if (err != null) {
|
||||||
logger.warn({ err }, 'error setting up login data')
|
logger.warn({ err }, 'error setting up login data')
|
||||||
|
@ -495,12 +500,14 @@ function _loginAsyncHandlers(req, user) {
|
||||||
LoginRateLimiter.recordSuccessfulLogin(user.email)
|
LoginRateLimiter.recordSuccessfulLogin(user.email)
|
||||||
AuthenticationController._recordSuccessfulLogin(user._id)
|
AuthenticationController._recordSuccessfulLogin(user._id)
|
||||||
AuthenticationController.ipMatchCheck(req, user)
|
AuthenticationController.ipMatchCheck(req, user)
|
||||||
Analytics.recordEvent(user._id, 'user-logged-in')
|
Analytics.recordEventForUser(user._id, 'user-logged-in')
|
||||||
Analytics.identifyUser(user._id, req.sessionID)
|
Analytics.identifyUser(user._id, anonymousAnalyticsId, isNewUser)
|
||||||
|
|
||||||
logger.log(
|
logger.log(
|
||||||
{ email: user.email, user_id: user._id.toString() },
|
{ email: user.email, user_id: user._id.toString() },
|
||||||
'successful log in'
|
'successful log in'
|
||||||
)
|
)
|
||||||
|
|
||||||
req.session.justLoggedIn = true
|
req.session.justLoggedIn = true
|
||||||
// capture the request ip for use when creating the session
|
// capture the request ip for use when creating the session
|
||||||
return (user._login_req_ip = req.ip)
|
return (user._login_req_ip = req.ip)
|
||||||
|
|
|
@ -377,10 +377,14 @@ module.exports = CollaboratorsInviteController = {
|
||||||
'project:membership:changed',
|
'project:membership:changed',
|
||||||
{ invites: true, members: true }
|
{ invites: true, members: true }
|
||||||
)
|
)
|
||||||
AnalyticsManager.recordEvent(currentUser._id, 'project-invite-accept', {
|
AnalyticsManager.recordEventForUser(
|
||||||
|
currentUser._id,
|
||||||
|
'project-invite-accept',
|
||||||
|
{
|
||||||
projectId,
|
projectId,
|
||||||
userId: currentUser._id,
|
userId: currentUser._id,
|
||||||
})
|
}
|
||||||
|
)
|
||||||
if (req.xhr) {
|
if (req.xhr) {
|
||||||
return res.sendStatus(204) // Done async via project page notification
|
return res.sendStatus(204) // Done async via project page notification
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -780,7 +780,7 @@ const ProjectController = {
|
||||||
metrics.inc(metricName)
|
metrics.inc(metricName)
|
||||||
|
|
||||||
if (userId) {
|
if (userId) {
|
||||||
AnalyticsManager.recordEvent(userId, 'project-opened', {
|
AnalyticsManager.recordEventForUser(userId, 'project-opened', {
|
||||||
projectId: project._id,
|
projectId: project._id,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,12 +35,12 @@ async function createBlankProject(ownerId, projectName, attributes = {}) {
|
||||||
const isImport = attributes && attributes.overleaf
|
const isImport = attributes && attributes.overleaf
|
||||||
const project = await _createBlankProject(ownerId, projectName, attributes)
|
const project = await _createBlankProject(ownerId, projectName, attributes)
|
||||||
if (isImport) {
|
if (isImport) {
|
||||||
AnalyticsManager.recordEvent(ownerId, 'project-imported', {
|
AnalyticsManager.recordEventForUser(ownerId, 'project-imported', {
|
||||||
projectId: project._id,
|
projectId: project._id,
|
||||||
attributes,
|
attributes,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
AnalyticsManager.recordEvent(ownerId, 'project-created', {
|
AnalyticsManager.recordEventForUser(ownerId, 'project-created', {
|
||||||
projectId: project._id,
|
projectId: project._id,
|
||||||
attributes,
|
attributes,
|
||||||
})
|
})
|
||||||
|
@ -50,7 +50,7 @@ async function createBlankProject(ownerId, projectName, attributes = {}) {
|
||||||
|
|
||||||
async function createProjectFromSnippet(ownerId, projectName, docLines) {
|
async function createProjectFromSnippet(ownerId, projectName, docLines) {
|
||||||
const project = await _createBlankProject(ownerId, projectName)
|
const project = await _createBlankProject(ownerId, projectName)
|
||||||
AnalyticsManager.recordEvent(ownerId, 'project-created', {
|
AnalyticsManager.recordEventForUser(ownerId, 'project-created', {
|
||||||
projectId: project._id,
|
projectId: project._id,
|
||||||
})
|
})
|
||||||
await _createRootDoc(project, ownerId, docLines)
|
await _createRootDoc(project, ownerId, docLines)
|
||||||
|
@ -63,7 +63,7 @@ async function createBasicProject(ownerId, projectName) {
|
||||||
const docLines = await _buildTemplate('mainbasic.tex', ownerId, projectName)
|
const docLines = await _buildTemplate('mainbasic.tex', ownerId, projectName)
|
||||||
await _createRootDoc(project, ownerId, docLines)
|
await _createRootDoc(project, ownerId, docLines)
|
||||||
|
|
||||||
AnalyticsManager.recordEvent(ownerId, 'project-created', {
|
AnalyticsManager.recordEventForUser(ownerId, 'project-created', {
|
||||||
projectId: project._id,
|
projectId: project._id,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ async function createExampleProject(ownerId, projectName) {
|
||||||
|
|
||||||
await _addExampleProjectFiles(ownerId, projectName, project)
|
await _addExampleProjectFiles(ownerId, projectName, project)
|
||||||
|
|
||||||
AnalyticsManager.recordEvent(ownerId, 'project-created', {
|
AnalyticsManager.recordEventForUser(ownerId, 'project-created', {
|
||||||
projectId: project._id,
|
projectId: project._id,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -96,7 +96,7 @@ async function assignUserToVariant(userId, splitTest) {
|
||||||
[`splitTests.${splitTest.id}`]: selectedVariant,
|
[`splitTests.${splitTest.id}`]: selectedVariant,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
AnalyticsManager.setUserProperty(
|
AnalyticsManager.setUserPropertyForUser(
|
||||||
userId,
|
userId,
|
||||||
`split-test-${splitTest.id}`,
|
`split-test-${splitTest.id}`,
|
||||||
selectedVariant
|
selectedVariant
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
const UserGetter = require('../User/UserGetter')
|
const UserGetter = require('../User/UserGetter')
|
||||||
const UserUpdater = require('../User/UserUpdater')
|
const UserUpdater = require('../User/UserUpdater')
|
||||||
const AnalyticsManager = require('../Analytics/AnalyticsManager')
|
const AnalyticsManager = require('../Analytics/AnalyticsManager')
|
||||||
|
const UserAnalyticsIdCache = require('../Analytics/UserAnalyticsIdCache')
|
||||||
const crypto = require('crypto')
|
const crypto = require('crypto')
|
||||||
const _ = require('lodash')
|
const _ = require('lodash')
|
||||||
const { callbackify } = require('util')
|
const { callbackify } = require('util')
|
||||||
|
@ -24,7 +25,7 @@ const BETA_PHASE = 'beta'
|
||||||
* // execute the default behaviour (control group)
|
* // execute the default behaviour (control group)
|
||||||
* }
|
* }
|
||||||
* // then record an event
|
* // then record an event
|
||||||
* AnalyticsManager.recordEvent(userId, 'example-project-created', {
|
* AnalyticsManager.recordEventForUser(userId, 'example-project-created', {
|
||||||
* projectId: project._id,
|
* projectId: project._id,
|
||||||
* ...assignment.analytics.segmentation
|
* ...assignment.analytics.segmentation
|
||||||
* })
|
* })
|
||||||
|
@ -35,8 +36,50 @@ const BETA_PHASE = 'beta'
|
||||||
* @returns {Promise<{variant: string, analytics: {segmentation: {splitTest: string, variant: string, phase: string, versionNumber: number}|{}}}>}
|
* @returns {Promise<{variant: string, analytics: {segmentation: {splitTest: string, variant: string, phase: string, versionNumber: number}|{}}}>}
|
||||||
*/
|
*/
|
||||||
async function getAssignment(userId, splitTestName, options) {
|
async function getAssignment(userId, splitTestName, options) {
|
||||||
const splitTest = await splitTestCache.get(splitTestName)
|
if (!userId) {
|
||||||
|
return { variant: 'default', analytics: { segmentation: {} } }
|
||||||
|
}
|
||||||
|
const analyticsId = await UserAnalyticsIdCache.get(userId)
|
||||||
|
return _getAssignment(analyticsId, userId, undefined, splitTestName, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the assignment of a user to a split test by their session.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* // Assign user and record an event
|
||||||
|
*
|
||||||
|
* const assignment = await SplitTestV2Handler.getAssignment(req.session, 'example-project')
|
||||||
|
* if (assignment.variant === 'awesome-new-version') {
|
||||||
|
* // execute my awesome change
|
||||||
|
* }
|
||||||
|
* else {
|
||||||
|
* // execute the default behaviour (control group)
|
||||||
|
* }
|
||||||
|
* // then record an event
|
||||||
|
* AnalyticsManager.recordEventForSession(req.session, 'example-project-created', {
|
||||||
|
* projectId: project._id,
|
||||||
|
* ...assignment.analytics.segmentation
|
||||||
|
* })
|
||||||
|
*
|
||||||
|
* @param session the request session
|
||||||
|
* @param splitTestName the unique name of the split test
|
||||||
|
* @param options {Object<sync: boolean>} - for test purposes only, to force the synchronous update of the user's profile
|
||||||
|
* @returns {Promise<{variant: string, analytics: {segmentation: {splitTest: string, variant: string, phase: string, versionNumber: number}|{}}}>}
|
||||||
|
*/
|
||||||
|
async function getAssignmentForSession(session, splitTestName, options) {
|
||||||
|
const { userId, analyticsId } = AnalyticsManager.getIdsFromSession(session)
|
||||||
|
return _getAssignment(analyticsId, userId, session, splitTestName, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function _getAssignment(
|
||||||
|
analyticsId,
|
||||||
|
userId,
|
||||||
|
session,
|
||||||
|
splitTestName,
|
||||||
|
options
|
||||||
|
) {
|
||||||
|
const splitTest = await splitTestCache.get(splitTestName)
|
||||||
if (splitTest) {
|
if (splitTest) {
|
||||||
const currentVersion = splitTest.getCurrentVersion()
|
const currentVersion = splitTest.getCurrentVersion()
|
||||||
if (currentVersion.active) {
|
if (currentVersion.active) {
|
||||||
|
@ -45,10 +88,12 @@ async function getAssignment(userId, splitTestName, options) {
|
||||||
selectedVariantName,
|
selectedVariantName,
|
||||||
phase,
|
phase,
|
||||||
versionNumber,
|
versionNumber,
|
||||||
} = await _getAssignmentMetadata(userId, splitTest)
|
} = await _getAssignmentMetadata(analyticsId, userId, splitTest)
|
||||||
if (activeForUser) {
|
if (activeForUser) {
|
||||||
const assignmentConfig = {
|
const assignmentConfig = {
|
||||||
userId,
|
userId,
|
||||||
|
analyticsId,
|
||||||
|
session,
|
||||||
splitTestName,
|
splitTestName,
|
||||||
variantName: selectedVariantName,
|
variantName: selectedVariantName,
|
||||||
phase,
|
phase,
|
||||||
|
@ -89,10 +134,28 @@ async function assignInLocalsContext(res, userId, splitTestName, options) {
|
||||||
res.locals.splitTestVariants[splitTestName] = assignment.variant
|
res.locals.splitTestVariants[splitTestName] = assignment.variant
|
||||||
}
|
}
|
||||||
|
|
||||||
async function _getAssignmentMetadata(userId, splitTest) {
|
async function assignInLocalsContextForSession(
|
||||||
|
res,
|
||||||
|
session,
|
||||||
|
splitTestName,
|
||||||
|
options
|
||||||
|
) {
|
||||||
|
const assignment = await getAssignmentForSession(
|
||||||
|
session,
|
||||||
|
splitTestName,
|
||||||
|
options
|
||||||
|
)
|
||||||
|
if (!res.locals.splitTestVariants) {
|
||||||
|
res.locals.splitTestVariants = {}
|
||||||
|
}
|
||||||
|
res.locals.splitTestVariants[splitTestName] = assignment.variant
|
||||||
|
}
|
||||||
|
|
||||||
|
async function _getAssignmentMetadata(analyticsId, userId, splitTest) {
|
||||||
const currentVersion = splitTest.getCurrentVersion()
|
const currentVersion = splitTest.getCurrentVersion()
|
||||||
const phase = currentVersion.phase
|
const phase = currentVersion.phase
|
||||||
if ([ALPHA_PHASE, BETA_PHASE].includes(phase)) {
|
if ([ALPHA_PHASE, BETA_PHASE].includes(phase)) {
|
||||||
|
if (userId) {
|
||||||
const user = await _getUser(userId)
|
const user = await _getUser(userId)
|
||||||
if (
|
if (
|
||||||
(phase === ALPHA_PHASE && !(user && user.alphaProgram)) ||
|
(phase === ALPHA_PHASE && !(user && user.alphaProgram)) ||
|
||||||
|
@ -102,8 +165,13 @@ async function _getAssignmentMetadata(userId, splitTest) {
|
||||||
activeForUser: false,
|
activeForUser: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
activeForUser: false,
|
||||||
}
|
}
|
||||||
const percentile = _getPercentile(userId, splitTest.name, phase)
|
}
|
||||||
|
}
|
||||||
|
const percentile = _getPercentile(analyticsId, splitTest.name, phase)
|
||||||
const selectedVariantName = _getVariantFromPercentile(
|
const selectedVariantName = _getVariantFromPercentile(
|
||||||
currentVersion.variants,
|
currentVersion.variants,
|
||||||
percentile
|
percentile
|
||||||
|
@ -116,10 +184,10 @@ async function _getAssignmentMetadata(userId, splitTest) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function _getPercentile(userId, splitTestName, splitTestPhase) {
|
function _getPercentile(analyticsId, splitTestName, splitTestPhase) {
|
||||||
const hash = crypto
|
const hash = crypto
|
||||||
.createHash('md5')
|
.createHash('md5')
|
||||||
.update(userId + splitTestName + splitTestPhase)
|
.update(analyticsId + splitTestName + splitTestPhase)
|
||||||
.digest('hex')
|
.digest('hex')
|
||||||
const hashPrefix = hash.substr(0, 8)
|
const hashPrefix = hash.substr(0, 8)
|
||||||
return Math.floor(
|
return Math.floor(
|
||||||
|
@ -139,11 +207,20 @@ function _getVariantFromPercentile(variants, percentile) {
|
||||||
|
|
||||||
async function _updateVariantAssignment({
|
async function _updateVariantAssignment({
|
||||||
userId,
|
userId,
|
||||||
|
analyticsId,
|
||||||
|
session,
|
||||||
splitTestName,
|
splitTestName,
|
||||||
phase,
|
phase,
|
||||||
versionNumber,
|
versionNumber,
|
||||||
variantName,
|
variantName,
|
||||||
}) {
|
}) {
|
||||||
|
const persistedAssignment = {
|
||||||
|
variantName,
|
||||||
|
versionNumber,
|
||||||
|
phase,
|
||||||
|
assignedAt: new Date(),
|
||||||
|
}
|
||||||
|
if (userId) {
|
||||||
const user = await _getUser(userId)
|
const user = await _getUser(userId)
|
||||||
if (user) {
|
if (user) {
|
||||||
const assignedSplitTests = user.splitTests || []
|
const assignedSplitTests = user.splitTests || []
|
||||||
|
@ -152,16 +229,30 @@ async function _updateVariantAssignment({
|
||||||
if (!existingAssignment) {
|
if (!existingAssignment) {
|
||||||
await UserUpdater.promises.updateUser(userId, {
|
await UserUpdater.promises.updateUser(userId, {
|
||||||
$addToSet: {
|
$addToSet: {
|
||||||
[`splitTests.${splitTestName}`]: {
|
[`splitTests.${splitTestName}`]: persistedAssignment,
|
||||||
variantName,
|
|
||||||
versionNumber,
|
|
||||||
phase,
|
|
||||||
assignedAt: new Date(),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
AnalyticsManager.setUserProperty(
|
AnalyticsManager.setUserPropertyForAnalyticsId(
|
||||||
userId,
|
analyticsId,
|
||||||
|
`split-test-${splitTestName}-${versionNumber}`,
|
||||||
|
variantName
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (session) {
|
||||||
|
if (!session.splitTests) {
|
||||||
|
session.splitTests = {}
|
||||||
|
}
|
||||||
|
if (!session.splitTests[splitTestName]) {
|
||||||
|
session.splitTests[splitTestName] = []
|
||||||
|
}
|
||||||
|
const existingAssignment = _.find(session.splitTests[splitTestName], {
|
||||||
|
versionNumber,
|
||||||
|
})
|
||||||
|
if (!existingAssignment) {
|
||||||
|
session.splitTests[splitTestName].push(persistedAssignment)
|
||||||
|
AnalyticsManager.setUserPropertyForAnalyticsId(
|
||||||
|
analyticsId,
|
||||||
`split-test-${splitTestName}-${versionNumber}`,
|
`split-test-${splitTestName}-${versionNumber}`,
|
||||||
variantName
|
variantName
|
||||||
)
|
)
|
||||||
|
@ -179,9 +270,13 @@ async function _getUser(id) {
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
getAssignment: callbackify(getAssignment),
|
getAssignment: callbackify(getAssignment),
|
||||||
|
getAssignmentForSession: callbackify(getAssignmentForSession),
|
||||||
assignInLocalsContext: callbackify(assignInLocalsContext),
|
assignInLocalsContext: callbackify(assignInLocalsContext),
|
||||||
|
assignInLocalsContextForSession: callbackify(assignInLocalsContextForSession),
|
||||||
promises: {
|
promises: {
|
||||||
getAssignment,
|
getAssignment,
|
||||||
|
getAssignmentForSession,
|
||||||
assignInLocalsContext,
|
assignInLocalsContext,
|
||||||
|
assignInLocalsContextForSession,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ const FeaturesUpdater = {
|
||||||
const matchedFeatureSet = FeaturesUpdater._getMatchedFeatureSet(
|
const matchedFeatureSet = FeaturesUpdater._getMatchedFeatureSet(
|
||||||
features
|
features
|
||||||
)
|
)
|
||||||
AnalyticsManager.setUserProperty(
|
AnalyticsManager.setUserPropertyForUser(
|
||||||
userId,
|
userId,
|
||||||
'feature-set',
|
'feature-set',
|
||||||
matchedFeatureSet
|
matchedFeatureSet
|
||||||
|
|
|
@ -39,85 +39,133 @@ function sendRecurlyAnalyticsEvent(event, eventData) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function _sendSubscriptionStartedEvent(eventData) {
|
async function _sendSubscriptionStartedEvent(eventData) {
|
||||||
const userId = _getUserId(eventData)
|
const userId = _getUserId(eventData)
|
||||||
const { planCode, quantity, state, isTrial } = _getSubscriptionData(eventData)
|
const { planCode, quantity, state, isTrial } = _getSubscriptionData(eventData)
|
||||||
AnalyticsManager.recordEvent(userId, 'subscription-started', {
|
AnalyticsManager.recordEventForUser(userId, 'subscription-started', {
|
||||||
plan_code: planCode,
|
plan_code: planCode,
|
||||||
quantity,
|
quantity,
|
||||||
is_trial: isTrial,
|
is_trial: isTrial,
|
||||||
})
|
})
|
||||||
AnalyticsManager.setUserProperty(userId, 'subscription-plan-code', planCode)
|
AnalyticsManager.setUserPropertyForUser(
|
||||||
AnalyticsManager.setUserProperty(userId, 'subscription-state', state)
|
userId,
|
||||||
AnalyticsManager.setUserProperty(userId, 'subscription-is-trial', isTrial)
|
'subscription-plan-code',
|
||||||
|
planCode
|
||||||
|
)
|
||||||
|
AnalyticsManager.setUserPropertyForUser(userId, 'subscription-state', state)
|
||||||
|
AnalyticsManager.setUserPropertyForUser(
|
||||||
|
userId,
|
||||||
|
'subscription-is-trial',
|
||||||
|
isTrial
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function _sendSubscriptionUpdatedEvent(eventData) {
|
async function _sendSubscriptionUpdatedEvent(eventData) {
|
||||||
const userId = _getUserId(eventData)
|
const userId = _getUserId(eventData)
|
||||||
const { planCode, quantity, state, isTrial } = _getSubscriptionData(eventData)
|
const { planCode, quantity, state, isTrial } = _getSubscriptionData(eventData)
|
||||||
AnalyticsManager.recordEvent(userId, 'subscription-updated', {
|
AnalyticsManager.recordEventForUser(userId, 'subscription-updated', {
|
||||||
plan_code: planCode,
|
plan_code: planCode,
|
||||||
quantity,
|
quantity,
|
||||||
})
|
})
|
||||||
AnalyticsManager.setUserProperty(userId, 'subscription-plan-code', planCode)
|
AnalyticsManager.setUserPropertyForUser(
|
||||||
AnalyticsManager.setUserProperty(userId, 'subscription-state', state)
|
userId,
|
||||||
AnalyticsManager.setUserProperty(userId, 'subscription-is-trial', isTrial)
|
'subscription-plan-code',
|
||||||
|
planCode
|
||||||
|
)
|
||||||
|
AnalyticsManager.setUserPropertyForUser(userId, 'subscription-state', state)
|
||||||
|
AnalyticsManager.setUserPropertyForUser(
|
||||||
|
userId,
|
||||||
|
'subscription-is-trial',
|
||||||
|
isTrial
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function _sendSubscriptionCancelledEvent(eventData) {
|
async function _sendSubscriptionCancelledEvent(eventData) {
|
||||||
const userId = _getUserId(eventData)
|
const userId = _getUserId(eventData)
|
||||||
const { planCode, quantity, state, isTrial } = _getSubscriptionData(eventData)
|
const { planCode, quantity, state, isTrial } = _getSubscriptionData(eventData)
|
||||||
AnalyticsManager.recordEvent(userId, 'subscription-cancelled', {
|
AnalyticsManager.recordEventForUser(userId, 'subscription-cancelled', {
|
||||||
plan_code: planCode,
|
plan_code: planCode,
|
||||||
quantity,
|
quantity,
|
||||||
is_trial: isTrial,
|
is_trial: isTrial,
|
||||||
})
|
})
|
||||||
AnalyticsManager.setUserProperty(userId, 'subscription-state', state)
|
AnalyticsManager.setUserPropertyForUser(userId, 'subscription-state', state)
|
||||||
AnalyticsManager.setUserProperty(userId, 'subscription-is-trial', isTrial)
|
AnalyticsManager.setUserPropertyForUser(
|
||||||
|
userId,
|
||||||
|
'subscription-is-trial',
|
||||||
|
isTrial
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function _sendSubscriptionExpiredEvent(eventData) {
|
async function _sendSubscriptionExpiredEvent(eventData) {
|
||||||
const userId = _getUserId(eventData)
|
const userId = _getUserId(eventData)
|
||||||
const { planCode, quantity, state, isTrial } = _getSubscriptionData(eventData)
|
const { planCode, quantity, state, isTrial } = _getSubscriptionData(eventData)
|
||||||
AnalyticsManager.recordEvent(userId, 'subscription-expired', {
|
AnalyticsManager.recordEventForUser(userId, 'subscription-expired', {
|
||||||
plan_code: planCode,
|
plan_code: planCode,
|
||||||
quantity,
|
quantity,
|
||||||
is_trial: isTrial,
|
is_trial: isTrial,
|
||||||
})
|
})
|
||||||
AnalyticsManager.setUserProperty(userId, 'subscription-plan-code', planCode)
|
AnalyticsManager.setUserPropertyForUser(
|
||||||
AnalyticsManager.setUserProperty(userId, 'subscription-state', state)
|
userId,
|
||||||
AnalyticsManager.setUserProperty(userId, 'subscription-is-trial', isTrial)
|
'subscription-plan-code',
|
||||||
|
planCode
|
||||||
|
)
|
||||||
|
AnalyticsManager.setUserPropertyForUser(userId, 'subscription-state', state)
|
||||||
|
AnalyticsManager.setUserPropertyForUser(
|
||||||
|
userId,
|
||||||
|
'subscription-is-trial',
|
||||||
|
isTrial
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function _sendSubscriptionRenewedEvent(eventData) {
|
async function _sendSubscriptionRenewedEvent(eventData) {
|
||||||
const userId = _getUserId(eventData)
|
const userId = _getUserId(eventData)
|
||||||
const { planCode, quantity, state, isTrial } = _getSubscriptionData(eventData)
|
const { planCode, quantity, state, isTrial } = _getSubscriptionData(eventData)
|
||||||
AnalyticsManager.recordEvent(userId, 'subscription-renewed', {
|
AnalyticsManager.recordEventForUser(userId, 'subscription-renewed', {
|
||||||
plan_code: planCode,
|
plan_code: planCode,
|
||||||
quantity,
|
quantity,
|
||||||
is_trial: isTrial,
|
is_trial: isTrial,
|
||||||
})
|
})
|
||||||
AnalyticsManager.setUserProperty(userId, 'subscription-plan-code', planCode)
|
AnalyticsManager.setUserPropertyForUser(
|
||||||
AnalyticsManager.setUserProperty(userId, 'subscription-state', state)
|
userId,
|
||||||
AnalyticsManager.setUserProperty(userId, 'subscription-is-trial', isTrial)
|
'subscription-plan-code',
|
||||||
|
planCode
|
||||||
|
)
|
||||||
|
AnalyticsManager.setUserPropertyForUser(userId, 'subscription-state', state)
|
||||||
|
AnalyticsManager.setUserPropertyForUser(
|
||||||
|
userId,
|
||||||
|
'subscription-is-trial',
|
||||||
|
isTrial
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function _sendSubscriptionReactivatedEvent(eventData) {
|
async function _sendSubscriptionReactivatedEvent(eventData) {
|
||||||
const userId = _getUserId(eventData)
|
const userId = _getUserId(eventData)
|
||||||
const { planCode, quantity, state, isTrial } = _getSubscriptionData(eventData)
|
const { planCode, quantity, state, isTrial } = _getSubscriptionData(eventData)
|
||||||
AnalyticsManager.recordEvent(userId, 'subscription-reactivated', {
|
AnalyticsManager.recordEventForUser(userId, 'subscription-reactivated', {
|
||||||
plan_code: planCode,
|
plan_code: planCode,
|
||||||
quantity,
|
quantity,
|
||||||
})
|
})
|
||||||
AnalyticsManager.setUserProperty(userId, 'subscription-plan-code', planCode)
|
AnalyticsManager.setUserPropertyForUser(
|
||||||
AnalyticsManager.setUserProperty(userId, 'subscription-state', state)
|
userId,
|
||||||
AnalyticsManager.setUserProperty(userId, 'subscription-is-trial', isTrial)
|
'subscription-plan-code',
|
||||||
|
planCode
|
||||||
|
)
|
||||||
|
AnalyticsManager.setUserPropertyForUser(userId, 'subscription-state', state)
|
||||||
|
AnalyticsManager.setUserPropertyForUser(
|
||||||
|
userId,
|
||||||
|
'subscription-is-trial',
|
||||||
|
isTrial
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function _sendInvoicePaidEvent(eventData) {
|
async function _sendInvoicePaidEvent(eventData) {
|
||||||
const userId = _getUserId(eventData)
|
const userId = _getUserId(eventData)
|
||||||
AnalyticsManager.recordEvent(userId, 'subscription-invoice-collected')
|
AnalyticsManager.recordEventForUser(userId, 'subscription-invoice-collected')
|
||||||
AnalyticsManager.setUserProperty(userId, 'subscription-is-trial', false)
|
AnalyticsManager.setUserPropertyForUser(
|
||||||
|
userId,
|
||||||
|
'subscription-is-trial',
|
||||||
|
false
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function _getUserId(eventData) {
|
function _getUserId(eventData) {
|
||||||
|
|
|
@ -116,7 +116,7 @@ async function userSubscriptionPage(req, res) {
|
||||||
personalSubscription ? personalSubscription.plan : undefined
|
personalSubscription ? personalSubscription.plan : undefined
|
||||||
)
|
)
|
||||||
|
|
||||||
AnalyticsManager.recordEvent(user._id, 'subscription-page-view')
|
AnalyticsManager.recordEventForSession(req.session, 'subscription-page-view')
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
title: 'your_subscription',
|
title: 'your_subscription',
|
||||||
|
|
|
@ -360,7 +360,7 @@ async function _sendUserGroupPlanCodeUserProperty(userId) {
|
||||||
bestFeatures = plan.features
|
bestFeatures = plan.features
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AnalyticsManager.setUserProperty(
|
AnalyticsManager.setUserPropertyForUser(
|
||||||
userId,
|
userId,
|
||||||
'group-subscription-plan-code',
|
'group-subscription-plan-code',
|
||||||
bestPlanCode
|
bestPlanCode
|
||||||
|
|
|
@ -164,7 +164,9 @@ const TokenAccessHandler = {
|
||||||
addReadOnlyUserToProject(userId, projectId, callback) {
|
addReadOnlyUserToProject(userId, projectId, callback) {
|
||||||
userId = ObjectId(userId.toString())
|
userId = ObjectId(userId.toString())
|
||||||
projectId = ObjectId(projectId.toString())
|
projectId = ObjectId(projectId.toString())
|
||||||
Analytics.recordEvent(userId, 'project-joined', { mode: 'read-only' })
|
Analytics.recordEventForUser(userId, 'project-joined', {
|
||||||
|
mode: 'read-only',
|
||||||
|
})
|
||||||
Project.updateOne(
|
Project.updateOne(
|
||||||
{
|
{
|
||||||
_id: projectId,
|
_id: projectId,
|
||||||
|
@ -179,7 +181,9 @@ const TokenAccessHandler = {
|
||||||
addReadAndWriteUserToProject(userId, projectId, callback) {
|
addReadAndWriteUserToProject(userId, projectId, callback) {
|
||||||
userId = ObjectId(userId.toString())
|
userId = ObjectId(userId.toString())
|
||||||
projectId = ObjectId(projectId.toString())
|
projectId = ObjectId(projectId.toString())
|
||||||
Analytics.recordEvent(userId, 'project-joined', { mode: 'read-write' })
|
Analytics.recordEventForUser(userId, 'project-joined', {
|
||||||
|
mode: 'read-write',
|
||||||
|
})
|
||||||
Project.updateOne(
|
Project.updateOne(
|
||||||
{
|
{
|
||||||
_id: projectId,
|
_id: projectId,
|
||||||
|
|
|
@ -84,8 +84,16 @@ async function createNewUser(attributes, options = {}) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Analytics.recordEvent(user._id, 'user-registered')
|
await Analytics.recordEventForUser(user._id, 'user-registered')
|
||||||
Analytics.setUserProperty(user._id, 'created-at', new Date())
|
await Analytics.setUserPropertyForUser(user._id, 'created-at', new Date())
|
||||||
|
await Analytics.setUserPropertyForUser(user._id, 'user-id', user._id)
|
||||||
|
if (attributes.analyticsId) {
|
||||||
|
await Analytics.setUserPropertyForUser(
|
||||||
|
user._id,
|
||||||
|
'analytics-id',
|
||||||
|
attributes.analyticsId
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if (Features.hasFeature('saas')) {
|
if (Features.hasFeature('saas')) {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -32,7 +32,7 @@ async function checkAffiliations(userId) {
|
||||||
)
|
)
|
||||||
|
|
||||||
if (hasCommonsAccountAffiliation) {
|
if (hasCommonsAccountAffiliation) {
|
||||||
await AnalyticsManager.setUserProperty(
|
await AnalyticsManager.setUserPropertyForUser(
|
||||||
userId,
|
userId,
|
||||||
'registered-from-commons-account',
|
'registered-from-commons-account',
|
||||||
true
|
true
|
||||||
|
|
|
@ -8,7 +8,6 @@ const logger = require('logger-sharelatex')
|
||||||
const crypto = require('crypto')
|
const crypto = require('crypto')
|
||||||
const EmailHandler = require('../Email/EmailHandler')
|
const EmailHandler = require('../Email/EmailHandler')
|
||||||
const OneTimeTokenHandler = require('../Security/OneTimeTokenHandler')
|
const OneTimeTokenHandler = require('../Security/OneTimeTokenHandler')
|
||||||
const Analytics = require('../Analytics/AnalyticsManager')
|
|
||||||
const settings = require('@overleaf/settings')
|
const settings = require('@overleaf/settings')
|
||||||
const EmailHelper = require('../Helpers/EmailHelper')
|
const EmailHelper = require('../Helpers/EmailHelper')
|
||||||
|
|
||||||
|
@ -31,6 +30,7 @@ const UserRegistrationHandler = {
|
||||||
email: userDetails.email,
|
email: userDetails.email,
|
||||||
first_name: userDetails.first_name,
|
first_name: userDetails.first_name,
|
||||||
last_name: userDetails.last_name,
|
last_name: userDetails.last_name,
|
||||||
|
analyticsId: userDetails.analyticsId,
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
callback
|
callback
|
||||||
|
@ -87,7 +87,6 @@ const UserRegistrationHandler = {
|
||||||
}, // this can be slow, just fire it off
|
}, // this can be slow, just fire it off
|
||||||
],
|
],
|
||||||
error => {
|
error => {
|
||||||
Analytics.recordEvent(user._id, 'user-registered')
|
|
||||||
callback(error, user)
|
callback(error, user)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -15,6 +15,7 @@ const sessionsRedisClient = UserSessionsRedis.client()
|
||||||
|
|
||||||
const SessionAutostartMiddleware = require('./SessionAutostartMiddleware')
|
const SessionAutostartMiddleware = require('./SessionAutostartMiddleware')
|
||||||
const SessionStoreManager = require('./SessionStoreManager')
|
const SessionStoreManager = require('./SessionStoreManager')
|
||||||
|
const AnalyticsManager = require('../Features/Analytics/AnalyticsManager')
|
||||||
const session = require('express-session')
|
const session = require('express-session')
|
||||||
const RedisStore = require('connect-redis')(session)
|
const RedisStore = require('connect-redis')(session)
|
||||||
const bodyParser = require('./BodyParserWrapper')
|
const bodyParser = require('./BodyParserWrapper')
|
||||||
|
@ -32,6 +33,7 @@ const ProxyManager = require('./ProxyManager')
|
||||||
const translations = require('./Translations')
|
const translations = require('./Translations')
|
||||||
const Modules = require('./Modules')
|
const Modules = require('./Modules')
|
||||||
const Views = require('./Views')
|
const Views = require('./Views')
|
||||||
|
const Features = require('./Features')
|
||||||
|
|
||||||
const ErrorController = require('../Features/Errors/ErrorController')
|
const ErrorController = require('../Features/Errors/ErrorController')
|
||||||
const HttpErrorHandler = require('../Features/Errors/HttpErrorHandler')
|
const HttpErrorHandler = require('../Features/Errors/HttpErrorHandler')
|
||||||
|
@ -125,6 +127,9 @@ webRouter.use(
|
||||||
rolling: true,
|
rolling: true,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
if (Features.hasFeature('saas')) {
|
||||||
|
webRouter.use(AnalyticsManager.analyticsIdMiddleware)
|
||||||
|
}
|
||||||
|
|
||||||
// patch the session store to generate a validation token for every new session
|
// patch the session store to generate a validation token for every new session
|
||||||
SessionStoreManager.enableValidationToken(sessionStore)
|
SessionStoreManager.enableValidationToken(sessionStore)
|
||||||
|
|
|
@ -166,6 +166,7 @@ const UserSchema = new Schema({
|
||||||
onboardingEmailSentAt: { type: Date },
|
onboardingEmailSentAt: { type: Date },
|
||||||
auditLog: [AuditLogEntrySchema],
|
auditLog: [AuditLogEntrySchema],
|
||||||
splitTests: Schema.Types.Mixed,
|
splitTests: Schema.Types.Mixed,
|
||||||
|
analyticsId: { type: String },
|
||||||
})
|
})
|
||||||
|
|
||||||
exports.User = mongoose.model('User', UserSchema)
|
exports.User = mongoose.model('User', UserSchema)
|
||||||
|
|
|
@ -12,7 +12,7 @@ describe('AnalyticsController', function () {
|
||||||
|
|
||||||
this.AnalyticsManager = {
|
this.AnalyticsManager = {
|
||||||
updateEditingSession: sinon.stub(),
|
updateEditingSession: sinon.stub(),
|
||||||
recordEvent: sinon.stub(),
|
recordEventForSession: sinon.stub(),
|
||||||
}
|
}
|
||||||
|
|
||||||
this.Features = {
|
this.Features = {
|
||||||
|
@ -42,6 +42,7 @@ describe('AnalyticsController', function () {
|
||||||
params: {
|
params: {
|
||||||
projectId: 'a project id',
|
projectId: 'a project id',
|
||||||
},
|
},
|
||||||
|
session: {},
|
||||||
}
|
}
|
||||||
this.GeoIpLookup.getDetails = sinon
|
this.GeoIpLookup.getDetails = sinon
|
||||||
.stub()
|
.stub()
|
||||||
|
@ -78,35 +79,18 @@ describe('AnalyticsController', function () {
|
||||||
delete this.expectedData._csrf
|
delete this.expectedData._csrf
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should use the user_id', function (done) {
|
it('should use the session', function (done) {
|
||||||
this.SessionManager.getLoggedInUserId.returns('1234')
|
|
||||||
this.controller.recordEvent(this.req, this.res)
|
this.controller.recordEvent(this.req, this.res)
|
||||||
this.AnalyticsManager.recordEvent
|
this.AnalyticsManager.recordEventForSession
|
||||||
.calledWith('1234', this.req.params.event, this.expectedData)
|
.calledWith(this.req.session, this.req.params.event, this.expectedData)
|
||||||
.should.equal(true)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should use the session id', function (done) {
|
|
||||||
this.controller.recordEvent(this.req, this.res)
|
|
||||||
this.AnalyticsManager.recordEvent
|
|
||||||
.calledWith(
|
|
||||||
this.req.sessionID,
|
|
||||||
this.req.params.event,
|
|
||||||
this.expectedData
|
|
||||||
)
|
|
||||||
.should.equal(true)
|
.should.equal(true)
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should remove the CSRF token before sending to the manager', function (done) {
|
it('should remove the CSRF token before sending to the manager', function (done) {
|
||||||
this.controller.recordEvent(this.req, this.res)
|
this.controller.recordEvent(this.req, this.res)
|
||||||
this.AnalyticsManager.recordEvent
|
this.AnalyticsManager.recordEventForSession
|
||||||
.calledWith(
|
.calledWith(this.req.session, this.req.params.event, this.expectedData)
|
||||||
this.req.sessionID,
|
|
||||||
this.req.params.event,
|
|
||||||
this.expectedData
|
|
||||||
)
|
|
||||||
.should.equal(true)
|
.should.equal(true)
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
|
|
|
@ -10,6 +10,7 @@ const MODULE_PATH = path.join(
|
||||||
describe('AnalyticsManager', function () {
|
describe('AnalyticsManager', function () {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
this.fakeUserId = '123abc'
|
this.fakeUserId = '123abc'
|
||||||
|
this.analyticsId = '123456'
|
||||||
this.Settings = {
|
this.Settings = {
|
||||||
analytics: { enabled: true },
|
analytics: { enabled: true },
|
||||||
}
|
}
|
||||||
|
@ -50,6 +51,9 @@ describe('AnalyticsManager', function () {
|
||||||
requires: {
|
requires: {
|
||||||
'@overleaf/settings': this.Settings,
|
'@overleaf/settings': this.Settings,
|
||||||
'../../infrastructure/Queues': this.Queues,
|
'../../infrastructure/Queues': this.Queues,
|
||||||
|
'./UserAnalyticsIdCache': (this.UserAnalyticsIdCache = {
|
||||||
|
get: sinon.stub().resolves(this.analyticsId),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -70,21 +74,26 @@ describe('AnalyticsManager', function () {
|
||||||
|
|
||||||
describe('queues the appropriate message for', function () {
|
describe('queues the appropriate message for', function () {
|
||||||
it('identifyUser', function () {
|
it('identifyUser', function () {
|
||||||
const oldUserId = '456def'
|
const analyticsId = '456def'
|
||||||
this.AnalyticsManager.identifyUser(this.fakeUserId, oldUserId)
|
this.AnalyticsManager.identifyUser(this.fakeUserId, analyticsId)
|
||||||
sinon.assert.calledWithMatch(this.analyticsEventsQueue.add, 'identify', {
|
sinon.assert.calledWithMatch(this.analyticsEventsQueue.add, 'identify', {
|
||||||
userId: this.fakeUserId,
|
userId: this.fakeUserId,
|
||||||
oldUserId,
|
analyticsId,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('recordEvent', function () {
|
it('recordEventForUser', async function () {
|
||||||
const event = 'fake-event'
|
const event = 'fake-event'
|
||||||
this.AnalyticsManager.recordEvent(this.fakeUserId, event, null)
|
await this.AnalyticsManager.recordEventForUser(
|
||||||
sinon.assert.calledWithMatch(this.analyticsEventsQueue.add, 'event', {
|
this.fakeUserId,
|
||||||
|
event,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
sinon.assert.calledWithMatch(this.analyticsEventsQueue.add, 'event', {
|
||||||
|
analyticsId: this.analyticsId,
|
||||||
event,
|
event,
|
||||||
userId: this.fakeUserId,
|
|
||||||
segmentation: null,
|
segmentation: null,
|
||||||
|
isLoggedIn: true,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ describe('AuthenticationController', function () {
|
||||||
this.res = new MockResponse()
|
this.res = new MockResponse()
|
||||||
this.callback = sinon.stub()
|
this.callback = sinon.stub()
|
||||||
this.next = sinon.stub()
|
this.next = sinon.stub()
|
||||||
|
this.req.session.analyticsId = 'abc-123'
|
||||||
|
|
||||||
this.AuthenticationController = SandboxedModule.require(modulePath, {
|
this.AuthenticationController = SandboxedModule.require(modulePath, {
|
||||||
requires: {
|
requires: {
|
||||||
|
@ -53,8 +54,9 @@ describe('AuthenticationController', function () {
|
||||||
setupLoginData: sinon.stub(),
|
setupLoginData: sinon.stub(),
|
||||||
}),
|
}),
|
||||||
'../Analytics/AnalyticsManager': (this.AnalyticsManager = {
|
'../Analytics/AnalyticsManager': (this.AnalyticsManager = {
|
||||||
recordEvent: sinon.stub(),
|
recordEventForUser: sinon.stub(),
|
||||||
identifyUser: sinon.stub(),
|
identifyUser: sinon.stub(),
|
||||||
|
getIdsFromSession: sinon.stub().returns({ userId: this.user._id }),
|
||||||
}),
|
}),
|
||||||
'../../infrastructure/SessionStoreManager': (this.SessionStoreManager = {}),
|
'../../infrastructure/SessionStoreManager': (this.SessionStoreManager = {}),
|
||||||
'@overleaf/settings': (this.Settings = {
|
'@overleaf/settings': (this.Settings = {
|
||||||
|
@ -1236,9 +1238,11 @@ describe('AuthenticationController', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should call identifyUser', function () {
|
it('should call identifyUser', function () {
|
||||||
this.AnalyticsManager.identifyUser
|
sinon.assert.calledWith(
|
||||||
.calledWith(this.user._id, this.req.sessionID)
|
this.AnalyticsManager.identifyUser,
|
||||||
.should.equal(true)
|
this.user._id,
|
||||||
|
this.req.session.analyticsId
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should setup the user data in the background', function () {
|
it('should setup the user data in the background', function () {
|
||||||
|
@ -1271,9 +1275,11 @@ describe('AuthenticationController', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should track the login event', function () {
|
it('should track the login event', function () {
|
||||||
this.AnalyticsManager.recordEvent
|
sinon.assert.calledWith(
|
||||||
.calledWith(this.user._id, 'user-logged-in')
|
this.AnalyticsManager.recordEventForUser,
|
||||||
.should.equal(true)
|
this.user._id,
|
||||||
|
'user-logged-in'
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -24,7 +24,7 @@ const { ObjectId } = require('mongodb')
|
||||||
describe('CollaboratorsInviteController', function () {
|
describe('CollaboratorsInviteController', function () {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
this.user = { _id: 'id' }
|
this.user = { _id: 'id' }
|
||||||
this.AnalyticsManger = { recordEvent: sinon.stub() }
|
this.AnalyticsManger = { recordEventForUser: sinon.stub() }
|
||||||
this.sendingUser = null
|
this.sendingUser = null
|
||||||
this.AuthenticationController = {
|
this.AuthenticationController = {
|
||||||
getSessionUser: req => {
|
getSessionUser: req => {
|
||||||
|
|
|
@ -173,7 +173,7 @@ describe('ProjectController', function () {
|
||||||
.BrandVariationsHandler,
|
.BrandVariationsHandler,
|
||||||
'../ThirdPartyDataStore/TpdsProjectFlusher': this.TpdsProjectFlusher,
|
'../ThirdPartyDataStore/TpdsProjectFlusher': this.TpdsProjectFlusher,
|
||||||
'../../models/Project': {},
|
'../../models/Project': {},
|
||||||
'../Analytics/AnalyticsManager': { recordEvent: () => {} },
|
'../Analytics/AnalyticsManager': { recordEventForUser: () => {} },
|
||||||
'../../infrastructure/Modules': {
|
'../../infrastructure/Modules': {
|
||||||
hooks: { fire: sinon.stub().yields(null, []) },
|
hooks: { fire: sinon.stub().yields(null, []) },
|
||||||
},
|
},
|
||||||
|
|
|
@ -40,7 +40,7 @@ describe('FeaturesUpdater', function () {
|
||||||
'../Institutions/InstitutionsFeatures': (this.InstitutionsFeatures = {}),
|
'../Institutions/InstitutionsFeatures': (this.InstitutionsFeatures = {}),
|
||||||
'../User/UserGetter': (this.UserGetter = {}),
|
'../User/UserGetter': (this.UserGetter = {}),
|
||||||
'../Analytics/AnalyticsManager': (this.AnalyticsManager = {
|
'../Analytics/AnalyticsManager': (this.AnalyticsManager = {
|
||||||
setUserProperty: sinon.stub(),
|
setUserPropertyForUser: sinon.stub(),
|
||||||
}),
|
}),
|
||||||
'../../infrastructure/Modules': (this.Modules = {
|
'../../infrastructure/Modules': (this.Modules = {
|
||||||
hooks: { fire: sinon.stub() },
|
hooks: { fire: sinon.stub() },
|
||||||
|
@ -182,7 +182,7 @@ describe('FeaturesUpdater', function () {
|
||||||
)
|
)
|
||||||
|
|
||||||
sinon.assert.calledWith(
|
sinon.assert.calledWith(
|
||||||
this.AnalyticsManager.setUserProperty,
|
this.AnalyticsManager.setUserPropertyForUser,
|
||||||
this.user_id,
|
this.user_id,
|
||||||
'feature-set',
|
'feature-set',
|
||||||
'personal'
|
'personal'
|
||||||
|
@ -201,7 +201,7 @@ describe('FeaturesUpdater', function () {
|
||||||
)
|
)
|
||||||
|
|
||||||
sinon.assert.calledWith(
|
sinon.assert.calledWith(
|
||||||
this.AnalyticsManager.setUserProperty,
|
this.AnalyticsManager.setUserPropertyForUser,
|
||||||
this.user_id,
|
this.user_id,
|
||||||
'feature-set',
|
'feature-set',
|
||||||
'mixed'
|
'mixed'
|
||||||
|
|
|
@ -27,8 +27,8 @@ describe('RecurlyEventHandler', function () {
|
||||||
this.RecurlyEventHandler = SandboxedModule.require(modulePath, {
|
this.RecurlyEventHandler = SandboxedModule.require(modulePath, {
|
||||||
requires: {
|
requires: {
|
||||||
'../Analytics/AnalyticsManager': (this.AnalyticsManager = {
|
'../Analytics/AnalyticsManager': (this.AnalyticsManager = {
|
||||||
recordEvent: sinon.stub(),
|
recordEventForUser: sinon.stub(),
|
||||||
setUserProperty: sinon.stub(),
|
setUserPropertyForUser: sinon.stub(),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -40,7 +40,7 @@ describe('RecurlyEventHandler', function () {
|
||||||
this.eventData
|
this.eventData
|
||||||
)
|
)
|
||||||
sinon.assert.calledWith(
|
sinon.assert.calledWith(
|
||||||
this.AnalyticsManager.recordEvent,
|
this.AnalyticsManager.recordEventForUser,
|
||||||
this.userId,
|
this.userId,
|
||||||
'subscription-started',
|
'subscription-started',
|
||||||
{
|
{
|
||||||
|
@ -50,19 +50,19 @@ describe('RecurlyEventHandler', function () {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
sinon.assert.calledWith(
|
sinon.assert.calledWith(
|
||||||
this.AnalyticsManager.setUserProperty,
|
this.AnalyticsManager.setUserPropertyForUser,
|
||||||
this.userId,
|
this.userId,
|
||||||
'subscription-plan-code',
|
'subscription-plan-code',
|
||||||
this.planCode
|
this.planCode
|
||||||
)
|
)
|
||||||
sinon.assert.calledWith(
|
sinon.assert.calledWith(
|
||||||
this.AnalyticsManager.setUserProperty,
|
this.AnalyticsManager.setUserPropertyForUser,
|
||||||
this.userId,
|
this.userId,
|
||||||
'subscription-state',
|
'subscription-state',
|
||||||
'active'
|
'active'
|
||||||
)
|
)
|
||||||
sinon.assert.calledWith(
|
sinon.assert.calledWith(
|
||||||
this.AnalyticsManager.setUserProperty,
|
this.AnalyticsManager.setUserPropertyForUser,
|
||||||
this.userId,
|
this.userId,
|
||||||
'subscription-is-trial',
|
'subscription-is-trial',
|
||||||
true
|
true
|
||||||
|
@ -83,7 +83,7 @@ describe('RecurlyEventHandler', function () {
|
||||||
this.eventData
|
this.eventData
|
||||||
)
|
)
|
||||||
sinon.assert.calledWith(
|
sinon.assert.calledWith(
|
||||||
this.AnalyticsManager.recordEvent,
|
this.AnalyticsManager.recordEventForUser,
|
||||||
this.userId,
|
this.userId,
|
||||||
'subscription-started',
|
'subscription-started',
|
||||||
{
|
{
|
||||||
|
@ -93,13 +93,13 @@ describe('RecurlyEventHandler', function () {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
sinon.assert.calledWith(
|
sinon.assert.calledWith(
|
||||||
this.AnalyticsManager.setUserProperty,
|
this.AnalyticsManager.setUserPropertyForUser,
|
||||||
this.userId,
|
this.userId,
|
||||||
'subscription-state',
|
'subscription-state',
|
||||||
'active'
|
'active'
|
||||||
)
|
)
|
||||||
sinon.assert.calledWith(
|
sinon.assert.calledWith(
|
||||||
this.AnalyticsManager.setUserProperty,
|
this.AnalyticsManager.setUserPropertyForUser,
|
||||||
this.userId,
|
this.userId,
|
||||||
'subscription-is-trial',
|
'subscription-is-trial',
|
||||||
false
|
false
|
||||||
|
@ -114,7 +114,7 @@ describe('RecurlyEventHandler', function () {
|
||||||
this.eventData
|
this.eventData
|
||||||
)
|
)
|
||||||
sinon.assert.calledWith(
|
sinon.assert.calledWith(
|
||||||
this.AnalyticsManager.recordEvent,
|
this.AnalyticsManager.recordEventForUser,
|
||||||
this.userId,
|
this.userId,
|
||||||
'subscription-updated',
|
'subscription-updated',
|
||||||
{
|
{
|
||||||
|
@ -123,19 +123,19 @@ describe('RecurlyEventHandler', function () {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
sinon.assert.calledWith(
|
sinon.assert.calledWith(
|
||||||
this.AnalyticsManager.setUserProperty,
|
this.AnalyticsManager.setUserPropertyForUser,
|
||||||
this.userId,
|
this.userId,
|
||||||
'subscription-plan-code',
|
'subscription-plan-code',
|
||||||
this.planCode
|
this.planCode
|
||||||
)
|
)
|
||||||
sinon.assert.calledWith(
|
sinon.assert.calledWith(
|
||||||
this.AnalyticsManager.setUserProperty,
|
this.AnalyticsManager.setUserPropertyForUser,
|
||||||
this.userId,
|
this.userId,
|
||||||
'subscription-state',
|
'subscription-state',
|
||||||
'active'
|
'active'
|
||||||
)
|
)
|
||||||
sinon.assert.calledWith(
|
sinon.assert.calledWith(
|
||||||
this.AnalyticsManager.setUserProperty,
|
this.AnalyticsManager.setUserPropertyForUser,
|
||||||
this.userId,
|
this.userId,
|
||||||
'subscription-is-trial',
|
'subscription-is-trial',
|
||||||
true
|
true
|
||||||
|
@ -149,7 +149,7 @@ describe('RecurlyEventHandler', function () {
|
||||||
this.eventData
|
this.eventData
|
||||||
)
|
)
|
||||||
sinon.assert.calledWith(
|
sinon.assert.calledWith(
|
||||||
this.AnalyticsManager.recordEvent,
|
this.AnalyticsManager.recordEventForUser,
|
||||||
this.userId,
|
this.userId,
|
||||||
'subscription-cancelled',
|
'subscription-cancelled',
|
||||||
{
|
{
|
||||||
|
@ -159,13 +159,13 @@ describe('RecurlyEventHandler', function () {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
sinon.assert.calledWith(
|
sinon.assert.calledWith(
|
||||||
this.AnalyticsManager.setUserProperty,
|
this.AnalyticsManager.setUserPropertyForUser,
|
||||||
this.userId,
|
this.userId,
|
||||||
'subscription-state',
|
'subscription-state',
|
||||||
'cancelled'
|
'cancelled'
|
||||||
)
|
)
|
||||||
sinon.assert.calledWith(
|
sinon.assert.calledWith(
|
||||||
this.AnalyticsManager.setUserProperty,
|
this.AnalyticsManager.setUserPropertyForUser,
|
||||||
this.userId,
|
this.userId,
|
||||||
'subscription-is-trial',
|
'subscription-is-trial',
|
||||||
true
|
true
|
||||||
|
@ -179,7 +179,7 @@ describe('RecurlyEventHandler', function () {
|
||||||
this.eventData
|
this.eventData
|
||||||
)
|
)
|
||||||
sinon.assert.calledWith(
|
sinon.assert.calledWith(
|
||||||
this.AnalyticsManager.recordEvent,
|
this.AnalyticsManager.recordEventForUser,
|
||||||
this.userId,
|
this.userId,
|
||||||
'subscription-expired',
|
'subscription-expired',
|
||||||
{
|
{
|
||||||
|
@ -189,19 +189,19 @@ describe('RecurlyEventHandler', function () {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
sinon.assert.calledWith(
|
sinon.assert.calledWith(
|
||||||
this.AnalyticsManager.setUserProperty,
|
this.AnalyticsManager.setUserPropertyForUser,
|
||||||
this.userId,
|
this.userId,
|
||||||
'subscription-plan-code',
|
'subscription-plan-code',
|
||||||
this.planCode
|
this.planCode
|
||||||
)
|
)
|
||||||
sinon.assert.calledWith(
|
sinon.assert.calledWith(
|
||||||
this.AnalyticsManager.setUserProperty,
|
this.AnalyticsManager.setUserPropertyForUser,
|
||||||
this.userId,
|
this.userId,
|
||||||
'subscription-state',
|
'subscription-state',
|
||||||
'expired'
|
'expired'
|
||||||
)
|
)
|
||||||
sinon.assert.calledWith(
|
sinon.assert.calledWith(
|
||||||
this.AnalyticsManager.setUserProperty,
|
this.AnalyticsManager.setUserPropertyForUser,
|
||||||
this.userId,
|
this.userId,
|
||||||
'subscription-is-trial',
|
'subscription-is-trial',
|
||||||
true
|
true
|
||||||
|
@ -214,7 +214,7 @@ describe('RecurlyEventHandler', function () {
|
||||||
this.eventData
|
this.eventData
|
||||||
)
|
)
|
||||||
sinon.assert.calledWith(
|
sinon.assert.calledWith(
|
||||||
this.AnalyticsManager.recordEvent,
|
this.AnalyticsManager.recordEventForUser,
|
||||||
this.userId,
|
this.userId,
|
||||||
'subscription-renewed',
|
'subscription-renewed',
|
||||||
{
|
{
|
||||||
|
@ -231,7 +231,7 @@ describe('RecurlyEventHandler', function () {
|
||||||
this.eventData
|
this.eventData
|
||||||
)
|
)
|
||||||
sinon.assert.calledWith(
|
sinon.assert.calledWith(
|
||||||
this.AnalyticsManager.recordEvent,
|
this.AnalyticsManager.recordEventForUser,
|
||||||
this.userId,
|
this.userId,
|
||||||
'subscription-reactivated',
|
'subscription-reactivated',
|
||||||
{
|
{
|
||||||
|
@ -255,7 +255,7 @@ describe('RecurlyEventHandler', function () {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
sinon.assert.calledWith(
|
sinon.assert.calledWith(
|
||||||
this.AnalyticsManager.recordEvent,
|
this.AnalyticsManager.recordEventForUser,
|
||||||
this.userId,
|
this.userId,
|
||||||
'subscription-invoice-collected'
|
'subscription-invoice-collected'
|
||||||
)
|
)
|
||||||
|
@ -274,7 +274,7 @@ describe('RecurlyEventHandler', function () {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
sinon.assert.notCalled(this.AnalyticsManager.recordEvent)
|
sinon.assert.notCalled(this.AnalyticsManager.recordEventForUser)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('with closed_invoice_notification', function () {
|
it('with closed_invoice_notification', function () {
|
||||||
|
@ -291,7 +291,7 @@ describe('RecurlyEventHandler', function () {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
sinon.assert.calledWith(
|
sinon.assert.calledWith(
|
||||||
this.AnalyticsManager.recordEvent,
|
this.AnalyticsManager.recordEventForUser,
|
||||||
this.userId,
|
this.userId,
|
||||||
'subscription-invoice-collected'
|
'subscription-invoice-collected'
|
||||||
)
|
)
|
||||||
|
@ -310,6 +310,6 @@ describe('RecurlyEventHandler', function () {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
sinon.assert.notCalled(this.AnalyticsManager.recordEvent)
|
sinon.assert.notCalled(this.AnalyticsManager.recordEventForUser)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -141,8 +141,9 @@ describe('SubscriptionController', function () {
|
||||||
}),
|
}),
|
||||||
'./Errors': SubscriptionErrors,
|
'./Errors': SubscriptionErrors,
|
||||||
'../Analytics/AnalyticsManager': (this.AnalyticsManager = {
|
'../Analytics/AnalyticsManager': (this.AnalyticsManager = {
|
||||||
recordEvent: sinon.stub(),
|
recordEventForUser: sinon.stub(),
|
||||||
setUserProperty: sinon.stub(),
|
recordEventForSession: sinon.stub(),
|
||||||
|
setUserPropertyForUser: sinon.stub(),
|
||||||
}),
|
}),
|
||||||
'../SplitTests/SplitTestHandler': (this.SplitTestHandler = {
|
'../SplitTests/SplitTestHandler': (this.SplitTestHandler = {
|
||||||
getTestSegmentation: () => {},
|
getTestSegmentation: () => {},
|
||||||
|
|
|
@ -108,7 +108,7 @@ describe('SubscriptionHandler', function () {
|
||||||
|
|
||||||
this.EmailHandler = { sendEmail: sinon.stub() }
|
this.EmailHandler = { sendEmail: sinon.stub() }
|
||||||
|
|
||||||
this.AnalyticsManager = { recordEvent: sinon.stub() }
|
this.AnalyticsManager = { recordEventForUser: sinon.stub() }
|
||||||
|
|
||||||
this.PlansLocator = {
|
this.PlansLocator = {
|
||||||
findLocalPlanInSettings: sinon.stub().returns({ planCode: 'plan' }),
|
findLocalPlanInSettings: sinon.stub().returns({ planCode: 'plan' }),
|
||||||
|
|
|
@ -105,7 +105,7 @@ describe('SubscriptionUpdater', function () {
|
||||||
findOneAndUpdate: sinon.stub().yields(),
|
findOneAndUpdate: sinon.stub().yields(),
|
||||||
}
|
}
|
||||||
this.AnalyticsManager = {
|
this.AnalyticsManager = {
|
||||||
setUserProperty: sinon.stub(),
|
setUserPropertyForUser: sinon.stub(),
|
||||||
}
|
}
|
||||||
this.SubscriptionUpdater = SandboxedModule.require(modulePath, {
|
this.SubscriptionUpdater = SandboxedModule.require(modulePath, {
|
||||||
requires: {
|
requires: {
|
||||||
|
@ -527,7 +527,7 @@ describe('SubscriptionUpdater', function () {
|
||||||
this.otherUserId,
|
this.otherUserId,
|
||||||
() => {
|
() => {
|
||||||
sinon.assert.calledWith(
|
sinon.assert.calledWith(
|
||||||
this.AnalyticsManager.setUserProperty,
|
this.AnalyticsManager.setUserPropertyForUser,
|
||||||
this.otherUserId,
|
this.otherUserId,
|
||||||
'group-subscription-plan-code',
|
'group-subscription-plan-code',
|
||||||
'group_subscription'
|
'group_subscription'
|
||||||
|
@ -547,7 +547,7 @@ describe('SubscriptionUpdater', function () {
|
||||||
this.otherUserId
|
this.otherUserId
|
||||||
)
|
)
|
||||||
sinon.assert.calledWith(
|
sinon.assert.calledWith(
|
||||||
this.AnalyticsManager.setUserProperty,
|
this.AnalyticsManager.setUserPropertyForUser,
|
||||||
this.otherUserId,
|
this.otherUserId,
|
||||||
'group-subscription-plan-code',
|
'group-subscription-plan-code',
|
||||||
'group_subscription'
|
'group_subscription'
|
||||||
|
@ -564,7 +564,7 @@ describe('SubscriptionUpdater', function () {
|
||||||
this.otherUserId
|
this.otherUserId
|
||||||
)
|
)
|
||||||
sinon.assert.calledWith(
|
sinon.assert.calledWith(
|
||||||
this.AnalyticsManager.setUserProperty,
|
this.AnalyticsManager.setUserPropertyForUser,
|
||||||
this.otherUserId,
|
this.otherUserId,
|
||||||
'group-subscription-plan-code',
|
'group-subscription-plan-code',
|
||||||
'better_group_subscription'
|
'better_group_subscription'
|
||||||
|
@ -581,7 +581,7 @@ describe('SubscriptionUpdater', function () {
|
||||||
this.otherUserId
|
this.otherUserId
|
||||||
)
|
)
|
||||||
sinon.assert.calledWith(
|
sinon.assert.calledWith(
|
||||||
this.AnalyticsManager.setUserProperty,
|
this.AnalyticsManager.setUserPropertyForUser,
|
||||||
this.otherUserId,
|
this.otherUserId,
|
||||||
'group-subscription-plan-code',
|
'group-subscription-plan-code',
|
||||||
'better_group_subscription'
|
'better_group_subscription'
|
||||||
|
@ -622,7 +622,7 @@ describe('SubscriptionUpdater', function () {
|
||||||
this.otherUserId,
|
this.otherUserId,
|
||||||
() => {
|
() => {
|
||||||
sinon.assert.calledWith(
|
sinon.assert.calledWith(
|
||||||
this.AnalyticsManager.setUserProperty,
|
this.AnalyticsManager.setUserPropertyForUser,
|
||||||
this.otherUserId,
|
this.otherUserId,
|
||||||
'group-subscription-plan-code',
|
'group-subscription-plan-code',
|
||||||
null
|
null
|
||||||
|
@ -635,7 +635,7 @@ describe('SubscriptionUpdater', function () {
|
||||||
it('should set the group plan code user property when removing user from all groups', function (done) {
|
it('should set the group plan code user property when removing user from all groups', function (done) {
|
||||||
this.SubscriptionUpdater.removeUserFromAllGroups(this.otherUserId, () => {
|
this.SubscriptionUpdater.removeUserFromAllGroups(this.otherUserId, () => {
|
||||||
sinon.assert.calledWith(
|
sinon.assert.calledWith(
|
||||||
this.AnalyticsManager.setUserProperty,
|
this.AnalyticsManager.setUserPropertyForUser,
|
||||||
this.otherUserId,
|
this.otherUserId,
|
||||||
'group-subscription-plan-code',
|
'group-subscription-plan-code',
|
||||||
null
|
null
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||||
*/
|
*/
|
||||||
const SandboxedModule = require('sandboxed-module')
|
const SandboxedModule = require('sandboxed-module')
|
||||||
const assert = require('assert')
|
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const sinon = require('sinon')
|
const sinon = require('sinon')
|
||||||
const modulePath = path.join(
|
const modulePath = path.join(
|
||||||
|
@ -43,7 +42,7 @@ describe('TokenAccessHandler', function () {
|
||||||
}),
|
}),
|
||||||
crypto: (this.Crypto = require('crypto')),
|
crypto: (this.Crypto = require('crypto')),
|
||||||
'../Analytics/AnalyticsManager': (this.Analytics = {
|
'../Analytics/AnalyticsManager': (this.Analytics = {
|
||||||
recordEvent: sinon.stub(),
|
recordEventForUser: sinon.stub(),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
@ -138,7 +137,7 @@ describe('TokenAccessHandler', function () {
|
||||||
this.Project.updateOne.lastCall.args[1].$addToSet
|
this.Project.updateOne.lastCall.args[1].$addToSet
|
||||||
).to.have.keys('tokenAccessReadOnly_refs')
|
).to.have.keys('tokenAccessReadOnly_refs')
|
||||||
sinon.assert.calledWith(
|
sinon.assert.calledWith(
|
||||||
this.Analytics.recordEvent,
|
this.Analytics.recordEventForUser,
|
||||||
this.userId,
|
this.userId,
|
||||||
'project-joined',
|
'project-joined',
|
||||||
{ mode: 'read-only' }
|
{ mode: 'read-only' }
|
||||||
|
@ -199,7 +198,7 @@ describe('TokenAccessHandler', function () {
|
||||||
this.Project.updateOne.lastCall.args[1].$addToSet
|
this.Project.updateOne.lastCall.args[1].$addToSet
|
||||||
).to.have.keys('tokenAccessReadAndWrite_refs')
|
).to.have.keys('tokenAccessReadAndWrite_refs')
|
||||||
sinon.assert.calledWith(
|
sinon.assert.calledWith(
|
||||||
this.Analytics.recordEvent,
|
this.Analytics.recordEventForUser,
|
||||||
this.userId,
|
this.userId,
|
||||||
'project-joined',
|
'project-joined',
|
||||||
{ mode: 'read-write' }
|
{ mode: 'read-write' }
|
||||||
|
|
|
@ -24,6 +24,7 @@ describe('UserController', function () {
|
||||||
_id: this.user_id,
|
_id: this.user_id,
|
||||||
email: 'old@something.com',
|
email: 'old@something.com',
|
||||||
},
|
},
|
||||||
|
analyticsId: this.user_id,
|
||||||
},
|
},
|
||||||
sessionID: '123',
|
sessionID: '123',
|
||||||
body: {},
|
body: {},
|
||||||
|
@ -544,9 +545,10 @@ describe('UserController', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should register the user and send them an email', function () {
|
it('should register the user and send them an email', function () {
|
||||||
this.UserRegistrationHandler.registerNewUserAndSendActivationEmail
|
sinon.assert.calledWith(
|
||||||
.calledWith(this.email)
|
this.UserRegistrationHandler.registerNewUserAndSendActivationEmail,
|
||||||
.should.equal(true)
|
this.email
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should return the user and activation url', function () {
|
it('should return the user and activation url', function () {
|
||||||
|
|
|
@ -42,8 +42,8 @@ describe('UserCreator', function () {
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
'../Analytics/AnalyticsManager': (this.Analytics = {
|
'../Analytics/AnalyticsManager': (this.Analytics = {
|
||||||
recordEvent: sinon.stub(),
|
recordEventForUser: sinon.stub(),
|
||||||
setUserProperty: sinon.stub(),
|
setUserPropertyForUser: sinon.stub(),
|
||||||
}),
|
}),
|
||||||
'./UserOnboardingEmailManager': (this.UserOnboardingEmailManager = {
|
'./UserOnboardingEmailManager': (this.UserOnboardingEmailManager = {
|
||||||
scheduleOnboardingEmail: sinon.stub(),
|
scheduleOnboardingEmail: sinon.stub(),
|
||||||
|
@ -271,12 +271,12 @@ describe('UserCreator', function () {
|
||||||
})
|
})
|
||||||
assert.equal(user.email, this.email)
|
assert.equal(user.email, this.email)
|
||||||
sinon.assert.calledWith(
|
sinon.assert.calledWith(
|
||||||
this.Analytics.recordEvent,
|
this.Analytics.recordEventForUser,
|
||||||
user._id,
|
user._id,
|
||||||
'user-registered'
|
'user-registered'
|
||||||
)
|
)
|
||||||
sinon.assert.calledWith(
|
sinon.assert.calledWith(
|
||||||
this.Analytics.setUserProperty,
|
this.Analytics.setUserPropertyForUser,
|
||||||
user._id,
|
user._id,
|
||||||
'created-at'
|
'created-at'
|
||||||
)
|
)
|
||||||
|
|
|
@ -36,7 +36,7 @@ describe('UserPostRegistrationAnalyticsManager', function () {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
this.AnalyticsManager = {
|
this.AnalyticsManager = {
|
||||||
setUserProperty: sinon.stub().resolves(),
|
setUserPropertyForUser: sinon.stub().resolves(),
|
||||||
}
|
}
|
||||||
this.UserPostRegistrationAnalyticsManager = SandboxedModule.require(
|
this.UserPostRegistrationAnalyticsManager = SandboxedModule.require(
|
||||||
MODULE_PATH,
|
MODULE_PATH,
|
||||||
|
@ -72,7 +72,8 @@ describe('UserPostRegistrationAnalyticsManager', function () {
|
||||||
)
|
)
|
||||||
expect(this.InstitutionsAPI.promises.getUserAffiliations).not.to.have.been
|
expect(this.InstitutionsAPI.promises.getUserAffiliations).not.to.have.been
|
||||||
.called
|
.called
|
||||||
expect(this.AnalyticsManager.setUserProperty).not.to.have.been.called
|
expect(this.AnalyticsManager.setUserPropertyForUser).not.to.have.been
|
||||||
|
.called
|
||||||
})
|
})
|
||||||
|
|
||||||
it('sets user property if user has commons account affiliationd', async function () {
|
it('sets user property if user has commons account affiliationd', async function () {
|
||||||
|
@ -92,7 +93,9 @@ describe('UserPostRegistrationAnalyticsManager', function () {
|
||||||
await this.UserPostRegistrationAnalyticsManager.postRegistrationAnalytics(
|
await this.UserPostRegistrationAnalyticsManager.postRegistrationAnalytics(
|
||||||
this.fakeUserId
|
this.fakeUserId
|
||||||
)
|
)
|
||||||
expect(this.AnalyticsManager.setUserProperty).to.have.been.calledWith(
|
expect(
|
||||||
|
this.AnalyticsManager.setUserPropertyForUser
|
||||||
|
).to.have.been.calledWith(
|
||||||
this.fakeUserId,
|
this.fakeUserId,
|
||||||
'registered-from-commons-account',
|
'registered-from-commons-account',
|
||||||
true
|
true
|
||||||
|
@ -110,7 +113,8 @@ describe('UserPostRegistrationAnalyticsManager', function () {
|
||||||
await this.UserPostRegistrationAnalyticsManager.postRegistrationAnalytics(
|
await this.UserPostRegistrationAnalyticsManager.postRegistrationAnalytics(
|
||||||
this.fakeUserId
|
this.fakeUserId
|
||||||
)
|
)
|
||||||
expect(this.AnalyticsManager.setUserProperty).not.to.have.been.called
|
expect(this.AnalyticsManager.setUserPropertyForUser).not.to.have.been
|
||||||
|
.called
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -24,7 +24,11 @@ const EmailHelper = require('../../../../app/src/Features/Helpers/EmailHelper')
|
||||||
|
|
||||||
describe('UserRegistrationHandler', function () {
|
describe('UserRegistrationHandler', function () {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
this.user = { _id: (this.user_id = '31j2lk21kjl') }
|
this.analyticsId = '123456'
|
||||||
|
this.user = {
|
||||||
|
_id: (this.user_id = '31j2lk21kjl'),
|
||||||
|
analyticsId: this.analyticsId,
|
||||||
|
}
|
||||||
this.User = { updateOne: sinon.stub().callsArgWith(2) }
|
this.User = { updateOne: sinon.stub().callsArgWith(2) }
|
||||||
this.UserGetter = { getUserByAnyEmail: sinon.stub() }
|
this.UserGetter = { getUserByAnyEmail: sinon.stub() }
|
||||||
this.UserCreator = {
|
this.UserCreator = {
|
||||||
|
@ -49,7 +53,9 @@ describe('UserRegistrationHandler', function () {
|
||||||
'../Email/EmailHandler': this.EmailHandler,
|
'../Email/EmailHandler': this.EmailHandler,
|
||||||
'../Security/OneTimeTokenHandler': this.OneTimeTokenHandler,
|
'../Security/OneTimeTokenHandler': this.OneTimeTokenHandler,
|
||||||
'../Analytics/AnalyticsManager': (this.AnalyticsManager = {
|
'../Analytics/AnalyticsManager': (this.AnalyticsManager = {
|
||||||
recordEvent: sinon.stub(),
|
recordEventForUser: sinon.stub(),
|
||||||
|
setUserPropertyForUser: sinon.stub(),
|
||||||
|
identifyUser: sinon.stub(),
|
||||||
}),
|
}),
|
||||||
'@overleaf/settings': (this.settings = {
|
'@overleaf/settings': (this.settings = {
|
||||||
siteUrl: 'http://sl.example.com',
|
siteUrl: 'http://sl.example.com',
|
||||||
|
@ -58,10 +64,11 @@ describe('UserRegistrationHandler', function () {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
return (this.passingRequest = {
|
this.passingRequest = {
|
||||||
email: 'something@email.com',
|
email: 'something@email.com',
|
||||||
password: '123',
|
password: '123',
|
||||||
})
|
analyticsId: this.analyticsId,
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('validate Register Request', function () {
|
describe('validate Register Request', function () {
|
||||||
|
@ -167,16 +174,15 @@ describe('UserRegistrationHandler', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should create a new user', function (done) {
|
it('should create a new user', function (done) {
|
||||||
return this.handler.registerNewUser(this.passingRequest, err => {
|
this.handler.registerNewUser(this.passingRequest, err => {
|
||||||
this.UserCreator.createNewUser
|
sinon.assert.calledWith(this.UserCreator.createNewUser, {
|
||||||
.calledWith({
|
|
||||||
email: this.passingRequest.email,
|
email: this.passingRequest.email,
|
||||||
holdingAccount: false,
|
holdingAccount: false,
|
||||||
first_name: this.passingRequest.first_name,
|
first_name: this.passingRequest.first_name,
|
||||||
last_name: this.passingRequest.last_name,
|
last_name: this.passingRequest.last_name,
|
||||||
|
analyticsId: this.user.analyticsId,
|
||||||
})
|
})
|
||||||
.should.equal(true)
|
done()
|
||||||
return done()
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -227,15 +233,6 @@ describe('UserRegistrationHandler', function () {
|
||||||
return done()
|
return done()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should track the registration event', function (done) {
|
|
||||||
return this.handler.registerNewUser(this.passingRequest, err => {
|
|
||||||
this.AnalyticsManager.recordEvent
|
|
||||||
.calledWith(this.user._id, 'user-registered')
|
|
||||||
.should.equal(true)
|
|
||||||
return done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should call the ReferalAllocator', function (done) {
|
it('should call the ReferalAllocator', function (done) {
|
||||||
|
@ -270,12 +267,10 @@ describe('UserRegistrationHandler', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should ask the UserRegistrationHandler to register user', function () {
|
it('should ask the UserRegistrationHandler to register user', function () {
|
||||||
return this.handler.registerNewUser
|
sinon.assert.calledWith(this.handler.registerNewUser, {
|
||||||
.calledWith({
|
|
||||||
email: this.email,
|
email: this.email,
|
||||||
password: this.password,
|
password: this.password,
|
||||||
})
|
})
|
||||||
.should.equal(true)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should generate a new password reset token', function () {
|
it('should generate a new password reset token', function () {
|
||||||
|
|
Loading…
Reference in a new issue