Merge pull request #5679 from overleaf/ab-utm-tracking-fix

UTM Tracking

GitOrigin-RevId: 8e9f8372affce72b35f47786de40def77c08a514
This commit is contained in:
Alexandre Bourdin
2021-11-09 09:05:05 +00:00
committed by Copybot
parent 7836743754
commit 05c1ecdde0
6 changed files with 318 additions and 53 deletions
@@ -1,6 +1,5 @@
const RefererParser = require('referer-parser')
const { URL } = require('url')
const AnalyticsManager = require('./AnalyticsManager')
const RequestHelper = require('./RequestHelper')
function clearSource(session) {
if (session) {
@@ -9,57 +8,10 @@ function clearSource(session) {
}
}
const UTM_KEYS = [
'utm_campaign',
'utm_source',
'utm_term',
'utm_medium',
'utm_count',
]
function parseUtm(query) {
const utmValues = {}
for (const utmKey of UTM_KEYS) {
if (query[utmKey]) {
utmValues[utmKey] = query[utmKey]
}
}
return Object.keys(utmValues).length > 0 ? utmValues : null
}
function parseReferrer(referrer, url) {
if (!referrer) {
return {
medium: 'direct',
}
}
const parsedReferrer = new RefererParser(referrer, url)
const referrerValues = {
medium: parsedReferrer.medium,
source: parsedReferrer.referer || 'other',
}
if (referrerValues.medium === 'unknown') {
try {
const referrerHostname = new URL(referrer).hostname
if (referrerHostname) {
referrerValues.medium = 'link'
referrerValues.source = referrerHostname
}
} catch (error) {
// ignore referrer parsing errors
}
}
return referrerValues
}
function setInbound(session, url, query, referrer) {
const inboundSession = {
referrer: parseReferrer(referrer, url),
utm: parseUtm(query),
referrer: RequestHelper.parseReferrer(referrer, url),
utm: RequestHelper.parseUtm(query),
}
if (inboundSession.referrer || inboundSession.utm) {
@@ -123,7 +75,7 @@ function addUserProperties(userId, session) {
}
if (session.inbound.utm) {
for (const utmKey of UTM_KEYS) {
for (const utmKey of RequestHelper.UTM_KEYS) {
if (session.inbound.utm[utmKey]) {
AnalyticsManager.setUserPropertyForUser(
userId,
@@ -29,7 +29,7 @@ function setInbound() {
}
if (SessionManager.isUserLoggedIn(req.session)) {
return next() // don't store referrer if user is alread logged in
return next() // don't store referrer if user is already logged in
}
const referrer = req.header('referrer')
@@ -0,0 +1,61 @@
const _ = require('lodash')
const RequestHelper = require('./RequestHelper')
const AnalyticsManager = require('./AnalyticsManager')
const querystring = require('querystring')
const { URL } = require('url')
const Settings = require('@overleaf/settings')
const OError = require('@overleaf/o-error')
const logger = require('logger-sharelatex')
function recordUTMTags() {
return function (req, res, next) {
const query = req.query
try {
const utmValues = RequestHelper.parseUtm(query)
if (utmValues) {
const path = new URL(req.url, Settings.siteUrl).pathname
AnalyticsManager.recordEventForSession(req.session, 'page-view', {
path,
...utmValues,
})
const propertyValue = [
'utm_source',
'utm_medium',
'utm_campaign',
'utm_term',
]
.map(tag => utmValues[tag] || 'N/A')
.join(';')
AnalyticsManager.setUserPropertyForSession(
req.session,
'utm-tags',
propertyValue
)
// redirect to URL without UTM query params
const queryWithoutUtm = _.omit(query, RequestHelper.UTM_KEYS)
const queryString =
Object.keys(queryWithoutUtm).length > 0
? '?' + querystring.stringify(queryWithoutUtm)
: ''
return res.redirect(path + queryString)
}
} catch (error) {
// log errors and fail silently
OError.tag(error, 'failed to track UTM tags', {
query,
})
logger.warn({ error }, error.message)
}
next()
}
}
module.exports = {
recordUTMTags,
}
@@ -0,0 +1,55 @@
const RefererParser = require('referer-parser')
const { URL } = require('url')
const UTM_KEYS = [
'utm_campaign',
'utm_source',
'utm_term',
'utm_medium',
'utm_count',
]
function parseUtm(query) {
const utmValues = {}
for (const utmKey of UTM_KEYS) {
if (query[utmKey]) {
utmValues[utmKey] = query[utmKey]
}
}
return Object.keys(utmValues).length > 0 ? utmValues : null
}
function parseReferrer(referrer, url) {
if (!referrer) {
return {
medium: 'direct',
}
}
const parsedReferrer = new RefererParser(referrer, url)
const referrerValues = {
medium: parsedReferrer.medium,
source: parsedReferrer.referer || 'other',
}
if (referrerValues.medium === 'unknown') {
try {
const referrerHostname = new URL(referrer).hostname
if (referrerHostname) {
referrerValues.medium = 'link'
referrerValues.source = referrerHostname
}
} catch (error) {
// ignore referrer parsing errors
}
}
return referrerValues
}
module.exports = {
UTM_KEYS,
parseUtm,
parseReferrer,
}
+2
View File
@@ -50,6 +50,7 @@ const InstitutionsController = require('./Features/Institutions/InstitutionsCont
const UserMembershipRouter = require('./Features/UserMembership/UserMembershipRouter')
const SystemMessageController = require('./Features/SystemMessages/SystemMessageController')
const AnalyticsRegistrationSourceMiddleware = require('./Features/Analytics/AnalyticsRegistrationSourceMiddleware')
const AnalyticsUTMTrackingMiddleware = require('./Features/Analytics/AnalyticsUTMTrackingMiddleware')
const { Joi, validate } = require('./infrastructure/Validation')
const {
renderUnsupportedBrowserPage,
@@ -69,6 +70,7 @@ function initialize(webRouter, privateApiRouter, publicApiRouter) {
}
webRouter.get('*', AnalyticsRegistrationSourceMiddleware.setInbound())
webRouter.get('*', AnalyticsUTMTrackingMiddleware.recordUTMTags())
webRouter.get('/login', UserPagesController.loginPage)
AuthenticationController.addEndpointToLoginWhitelist('/login')