-
Notifications
You must be signed in to change notification settings - Fork 35
Expand file tree
/
Copy pathsegmentWrapper.js
More file actions
298 lines (261 loc) · 9.62 KB
/
Copy pathsegmentWrapper.js
File metadata and controls
298 lines (261 loc) · 9.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
// @ts-check
import {getAdobeMCVisitorID} from './repositories/adobeRepository.js'
import {
CONSENT_STATES,
getConsentState,
getGoogleConsentValue,
getGoogleClientId,
getGoogleSessionId,
setGoogleUserId,
sendGoogleConsents,
triggerGoogleAnalyticsInitEvent
} from './repositories/googleRepository.js'
import {getXandrId} from './repositories/xandrRepository.js'
import {getConfig} from './config.js'
import {USER_GDPR, CMP_TRACK_EVENT, checkAnalyticsGdprIsAccepted, getGdprPrivacyValue} from './tcf.js'
/* Default properties to be sent on all trackings */
const DEFAULT_PROPERTIES = {platform: 'web'}
/* Disabled integrations when no GDPR Privacy Value is true */
export const INTEGRATIONS_WHEN_NO_CONSENTS = {
All: false
}
export const INTEGRATIONS_WHEN_NO_CONSENTS_CMP_SUBMITTED = {
All: true
}
const getUserId = userId => {
const prefix = getConfig('userIdPrefix')
if (!prefix) {
return userId
}
if (typeof userId === 'number' || (typeof userId === 'string' && !userId.startsWith(prefix))) {
return `${prefix}${userId}`
}
return userId
}
/**
* Get default properties using the constant and the window.__mpi object if available
* @returns {{[key:string]: any}} Default properties to attach to track
*/
export const getDefaultProperties = () => ({
...DEFAULT_PROPERTIES,
...getConfig('defaultProperties')
})
/**
* Get all needed integrations depending on the gdprPrivacy value.
* One of them is the AdobeMarketingCloudVisitorId for Adobe Analytics integration.
* @param {object} param - Object with the gdprPrivacyValue and if it's a CMP Submitted event
*/
const getTrackIntegrations = async ({gdprPrivacyValue, event}) => {
const isGdprAccepted = checkAnalyticsGdprIsAccepted(gdprPrivacyValue)
let marketingCloudVisitorId
let sessionId
let clientId
try {
if (isGdprAccepted) {
marketingCloudVisitorId = await getAdobeMCVisitorID()
}
sessionId = await getGoogleSessionId()
clientId = await getGoogleClientId()
} catch (error) {
console.error(error)
}
triggerGoogleAnalyticsInitEvent()
const restOfIntegrations = getRestOfIntegrations({isGdprAccepted, event})
// If we don't have the user consents we remove all the integrations but Adobe Analytics nor GA4
return {
...restOfIntegrations,
'Adobe Analytics': marketingCloudVisitorId ? {marketingCloudVisitorId} : true,
'Google Analytics 4':
clientId && sessionId
? {
clientId,
sessionId
}
: true
}
}
/**
* Get Rest of integrations depending on the gdprPrivacy value and if it's a CMP Submitted event
* @param {object} param - Object with the isGdprAccepted and if it's a CMP Submitted event
* @returns {object} integrations
*/
export const getRestOfIntegrations = ({isGdprAccepted, event}) => {
const isCMPSubmittedEvent = event === 'CMP Submitted'
if (isCMPSubmittedEvent) {
return INTEGRATIONS_WHEN_NO_CONSENTS_CMP_SUBMITTED
}
return isGdprAccepted ? {} : INTEGRATIONS_WHEN_NO_CONSENTS
}
/**
* It returns externalIds to add to context
*
* @param {Object} param
* @param {Object} param.context previous context
* @param {String} param.xandrId xandrId to be included
* @returns
*/
const getExternalIds = ({context, xandrId}) => {
const shouldSendXandrId = getConfig('sendXandrId') !== false
const isValidXandrId = xandrId && parseInt(xandrId) !== 0
if (!shouldSendXandrId || !isValidXandrId) {
return {}
}
const SEGMENT_COLLECTION = 'users'
const SEGMENT_ENCODING = 'none'
const SEGMENT_TYPE = 'xandr_id'
const externalIds = [
...(context?.externalIds || []),
{
collection: SEGMENT_COLLECTION,
encoding: SEGMENT_ENCODING,
id: xandrId,
type: SEGMENT_TYPE
}
]
const uniqueExternalIds = externalIds.filter(
({id: idFilter, type: typeFilter}, index) =>
index === externalIds.findIndex(({id: idFind, type: typeFind}) => idFilter === idFind && typeFilter === typeFind)
)
return {externalIds: uniqueExternalIds}
}
/**
* Get consent value for Google Consent Mode
* @param {string} gdprValue
* @returns {string} consent value
*/
const getConsentValue = gdprValue => (gdprValue === USER_GDPR.ACCEPTED ? CONSENT_STATES.granted : CONSENT_STATES.denied)
/**
* Get data like traits and integrations to be added to the context object
* @param {object} context Context object with all the actual info
* @returns {Promise<object>} New context with all the previous info and the new one
*/
export const decorateContextWithNeededData = async ({event = '', context = {}}) => {
const gdprPrivacyValue = await getGdprPrivacyValue()
const {analytics: gdprPrivacyValueAnalytics, advertising: gdprPrivacyValueAdvertising} = gdprPrivacyValue || {}
const isGdprAccepted = checkAnalyticsGdprIsAccepted(gdprPrivacyValue)
const [integrations, xandrId] = await Promise.all([
getTrackIntegrations({gdprPrivacyValue, event}),
getXandrId({gdprPrivacyValueAdvertising})
])
const analyticsConsentValue = getGoogleConsentValue('analytics_storage') ?? getConsentValue(gdprPrivacyValueAnalytics)
const adUserDataConsentValue = getGoogleConsentValue('ad_user_data') ?? getConsentValue(gdprPrivacyValueAdvertising)
const adPersonalizationConsentValue =
getGoogleConsentValue('ad_personalization') ?? getConsentValue(gdprPrivacyValueAdvertising)
const adStorageConsentValue = getGoogleConsentValue('ad_storage') ?? getConsentValue(gdprPrivacyValueAdvertising)
if (!isGdprAccepted) {
context.integrations = {
...(context.integrations ?? {}),
Personas: false,
Webhooks: true,
Webhook: true
}
}
return {
...context,
...(!isGdprAccepted && {ip: '0.0.0.0'}),
...getExternalIds({context, xandrId}),
analytics_storage: getConsentState(),
clientVersion: `segment-wrapper@${process.env.VERSION ?? '0.0.0'}`,
gdpr_privacy: gdprPrivacyValueAnalytics,
gdpr_privacy_advertising: gdprPrivacyValueAdvertising,
google_consents: {
analytics_storage: analyticsConsentValue,
ad_user_data: adUserDataConsentValue,
ad_personalization: adPersonalizationConsentValue,
ad_storage: adStorageConsentValue
},
integrations: {
...context.integrations,
...integrations
}
}
}
/**
* The track method lets you record any actions your users perform.
* @param {string} event The name of the event you’re tracking
* @param {object} [properties] A dictionary of properties for the event.
* @param {object} [context] A dictionary of options.
* @param {function} [callback] A function executed after a short timeout, giving the browser time to make outbound requests first.
* @returns {Promise}
*/
const track = (event, properties, context = {}, callback) =>
new Promise(resolve => {
const initTrack = async () => {
const newContext = await decorateContextWithNeededData({context, event})
/**
* @deprecated Now we use `defaultContextProperties` middleware
* and put the info on the context object
*/
const newProperties = {
...getDefaultProperties(),
...properties
}
const newCallback = async (...args) => {
if (callback) callback(...args) // eslint-disable-line n/no-callback-literal
const [gdprPrivacyValue] = await Promise.all([getGdprPrivacyValue()])
if (checkAnalyticsGdprIsAccepted(gdprPrivacyValue)) {
resolve(...args)
} else {
resolve()
}
}
const needsConsentManagement = getConfig('googleAnalyticsConsentManagement')
if (needsConsentManagement && event === CMP_TRACK_EVENT) {
sendGoogleConsents('update', newContext.google_consents)
}
window.analytics.track(
event,
newProperties,
{
...newContext,
context: {
integrations: {
...newContext.integrations
}
}
},
newCallback
)
}
initTrack()
})
/**
* Associate your users and their actions to a recognizable userId and traits.
* @param {string} userIdParam Id to identify the user.
* @param {object} traits A dictionary of traits you know about the user, like their email or name.
* @param {object} [options] A dictionary of options.
* @param {function} [callback] A function executed after a short timeout, giving the browser time to make outbound requests first.
* @returns {Promise}
*/
const identify = async (userIdParam, traits, options, callback) => {
const gdprPrivacyValue = await getGdprPrivacyValue()
const userId = getUserId(userIdParam)
setGoogleUserId(userId)
return window.analytics.identify(
userId,
checkAnalyticsGdprIsAccepted(gdprPrivacyValue) ? traits : {},
options,
callback
)
}
/**
* Record whenever a user sees a page of your website, along with any optional properties about the page.
* @param {string} event The name of the event you’re tracking
* @param {object=} properties A dictionary of properties for the event.
* @param {object} [context] A dictionary of options.
* @param {function} [callback] A function executed after a short timeout, giving the browser time to make outbound requests first.
* @returns {Promise}
*/
const page = (event, properties, context = {}, callback) => {
// we put a flag on context to know this track is a page
context.isPageTrack = true
// just call track again but the with the proper context
return track(event, properties, context, callback)
}
/**
* Resets the id, including anonymousId, and clear traits for the currently identified user and group.
* NOTE: Only clears the cookies and localStorage set by analytics.
* @returns {Promise}
*/
const reset = () => Promise.resolve(window.analytics.reset())
export default {page, identify, track, reset}