Add page_event_id to all successive events (#17738)
* Add `page_event_id` to all successive events * Update events.js * Break out event inits into functions so they don't break each other * Update events.js * Update events.js * Update events.js
This commit is contained in:
@@ -31,7 +31,6 @@ export function sendEvent ({
|
||||
type,
|
||||
version = '1.0.0',
|
||||
page_render_duration,
|
||||
exit_page_id,
|
||||
exit_first_paint,
|
||||
exit_dom_interactive,
|
||||
exit_dom_complete,
|
||||
@@ -60,6 +59,7 @@ export function sendEvent ({
|
||||
user: getUserEventsId(),
|
||||
version,
|
||||
created: new Date().toISOString(),
|
||||
page_event_id: pageEventId,
|
||||
|
||||
// Content information
|
||||
path: location.pathname,
|
||||
@@ -84,7 +84,6 @@ export function sendEvent ({
|
||||
page_render_duration,
|
||||
|
||||
// Exit event
|
||||
exit_page_id,
|
||||
exit_first_paint,
|
||||
exit_dom_interactive,
|
||||
exit_dom_complete,
|
||||
@@ -149,7 +148,6 @@ function trackScroll () {
|
||||
function sendExit () {
|
||||
if (sentExit) return
|
||||
if (document.visibilityState !== 'hidden') return
|
||||
if (!pageEventId) return
|
||||
sentExit = true
|
||||
const {
|
||||
firstContentfulPaint,
|
||||
@@ -158,7 +156,6 @@ function sendExit () {
|
||||
} = getPerformance()
|
||||
return sendEvent({
|
||||
type: 'exit',
|
||||
exit_page_id: pageEventId,
|
||||
exit_first_paint: firstContentfulPaint,
|
||||
exit_dom_interactive: domInteractive,
|
||||
exit_dom_complete: domComplete,
|
||||
@@ -167,22 +164,24 @@ function sendExit () {
|
||||
})
|
||||
}
|
||||
|
||||
export default function initializeEvents () {
|
||||
// Page event
|
||||
function initPageEvent () {
|
||||
const { render } = getPerformance()
|
||||
const pageEvent = sendEvent({
|
||||
type: 'page',
|
||||
page_render_duration: render
|
||||
})
|
||||
pageEventId = pageEvent?.context?.event_id
|
||||
}
|
||||
|
||||
// Clipboard event
|
||||
;['copy', 'cut', 'paste'].forEach(verb => {
|
||||
function initClipboardEvent () {
|
||||
['copy', 'cut', 'paste'].forEach(verb => {
|
||||
document.documentElement.addEventListener(verb, () => {
|
||||
sendEvent({ type: 'clipboard', clipboard_operation: verb })
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Link event
|
||||
function initLinkEvent () {
|
||||
document.documentElement.addEventListener('click', evt => {
|
||||
const link = evt.target.closest('a[href^="http"]')
|
||||
if (!link) return
|
||||
@@ -191,10 +190,16 @@ export default function initializeEvents () {
|
||||
link_url: link.href
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function initExitEvent () {
|
||||
window.addEventListener('scroll', trackScroll)
|
||||
document.addEventListener('visibilitychange', sendExit)
|
||||
}
|
||||
|
||||
function initNavigateEvent () {
|
||||
if (!document.querySelector('.sidebar-products')) return
|
||||
|
||||
// Navigate event
|
||||
Array.from(
|
||||
document.querySelectorAll('.sidebar-products details')
|
||||
).forEach(details => details.addEventListener(
|
||||
@@ -213,9 +218,17 @@ export default function initializeEvents () {
|
||||
navigate_label: `link: ${link.href}`
|
||||
})
|
||||
})
|
||||
|
||||
// Exit event
|
||||
pageEventId = pageEvent?.context?.event_id
|
||||
window.addEventListener('scroll', trackScroll)
|
||||
document.addEventListener('visibilitychange', sendExit)
|
||||
}
|
||||
|
||||
export default function initializeEvents () {
|
||||
initPageEvent() // must come first
|
||||
initExitEvent()
|
||||
initLinkEvent()
|
||||
initClipboardEvent()
|
||||
initNavigateEvent()
|
||||
// print event in ./print.js
|
||||
// survey event in ./helpfulness.js
|
||||
// experiment event in ./experiment.js
|
||||
// search event in ./search.js
|
||||
// redirect event in middleware/record-redirect.js
|
||||
}
|
||||
|
||||
@@ -32,6 +32,11 @@ const context = {
|
||||
format: 'date-time',
|
||||
description: 'The time we created the event; please reference UTC.'
|
||||
},
|
||||
page_event_id: {
|
||||
type: 'string',
|
||||
description: 'The id of the corresponding `page` event.',
|
||||
format: 'uuid'
|
||||
},
|
||||
|
||||
// Content information
|
||||
path: {
|
||||
@@ -132,8 +137,7 @@ const exitSchema = {
|
||||
additionalProperties: false,
|
||||
required: [
|
||||
'type',
|
||||
'context',
|
||||
'exit_page_id'
|
||||
'context'
|
||||
],
|
||||
properties: {
|
||||
context,
|
||||
@@ -141,12 +145,6 @@ const exitSchema = {
|
||||
type: 'string',
|
||||
pattern: '^exit$'
|
||||
},
|
||||
exit_page_id: {
|
||||
type: 'string',
|
||||
format: 'uuid',
|
||||
description: 'The value of the corresponding `page` event.'
|
||||
// id of the "page" event
|
||||
},
|
||||
exit_first_paint: {
|
||||
type: 'number',
|
||||
minimum: 0.001,
|
||||
|
||||
@@ -119,6 +119,16 @@ describe('POST /events', () => {
|
||||
}, 400)
|
||||
)
|
||||
|
||||
it('should allow page_event_id', () =>
|
||||
checkEvent({
|
||||
...pageExample,
|
||||
context: {
|
||||
...pageExample.context,
|
||||
page_event_id: baseExample.context.event_id
|
||||
}
|
||||
}, 201)
|
||||
)
|
||||
|
||||
it('should not allow a honeypot token', () =>
|
||||
checkEvent({
|
||||
...pageExample,
|
||||
@@ -281,7 +291,6 @@ describe('POST /events', () => {
|
||||
const exitExample = {
|
||||
...baseExample,
|
||||
type: 'exit',
|
||||
exit_page_id: 'c93c2d16-8e07-43d5-bc3c-eacc999c184d',
|
||||
exit_first_paint: 0.1,
|
||||
exit_dom_interactive: 0.2,
|
||||
exit_dom_complete: 0.3,
|
||||
@@ -293,14 +302,6 @@ describe('POST /events', () => {
|
||||
checkEvent(exitExample, 201)
|
||||
)
|
||||
|
||||
it('should require exit_page_id', () =>
|
||||
checkEvent({ ...exitExample, exit_page_id: undefined }, 400)
|
||||
)
|
||||
|
||||
it('should require exit_page_id is a uuid', () =>
|
||||
checkEvent({ ...exitExample, exit_page_id: 'afjdskalj' }, 400)
|
||||
)
|
||||
|
||||
it('exit_first_paint is a number', () =>
|
||||
checkEvent({ ...exitExample, exit_first_paint: 'afjdkl' }, 400)
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user