Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Sending events to POST /events endpoint (#15796)
  • Loading branch information
heiskr authored Oct 8, 2020
commit 7c0c493c35086d1f274af5c3ae4432d2e3e58fdf
118 changes: 118 additions & 0 deletions javascripts/events.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/* eslint-disable camelcase */
import { v4 as uuidv4 } from 'uuid'
import Cookies from 'js-cookie'
import getCsrf from './get-csrf'

const COOKIE_NAME = '_docs-events'

let cookieValue

export function getUserEventsId () {
if (cookieValue) return cookieValue
cookieValue = Cookies.get(COOKIE_NAME)
if (cookieValue) return cookieValue
cookieValue = uuidv4()
Cookies.set(COOKIE_NAME, cookieValue, {
secure: true,
sameSite: 'strict',
expires: 365
})
return cookieValue
}

export async function sendEvent ({
type,
version = '1.0.0',
page_render_duration,
exit_page_id,
exit_first_paint,
exit_dom_interactive,
exit_dom_complete,
exit_visit_duration,
exit_scroll_length,
link_url,
search_query,
search_context,
navigate_label,
survey_vote,
survey_comment,
survey_email,
experiment_name,
experiment_variation,
experiment_success
}) {
const response = await fetch('/events', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'CSRF-Token': getCsrf()
},
body: JSON.stringify({
type, // One of page, exit, link, search, navigate, survey, experiment

context: {
// Primitives
event_id: uuidv4(),
user: getUserEventsId(),
version,
created: new Date().toISOString(),

// Content information
path: location.pathname,
referrer: document.referrer,
search: location.search,
href: location.href,
site_language: location.pathname.split('/')[1],

// Device information
// os:
// os_version:
// browser:
// browser_version:
viewport_width: document.documentElement.clientWidth,
viewport_height: document.documentElement.clientHeight,

// Location information
timezone: new Date().getTimezoneOffset() / -60,
user_language: navigator.language
},

// Page event
page_render_duration,

// Exit event
exit_page_id,
exit_first_paint,
exit_dom_interactive,
exit_dom_complete,
exit_visit_duration,
exit_scroll_length,

// Link event
link_url,

// Search event
search_query,
search_context,

// Navigate event
navigate_label,

// Survey event
survey_vote,
survey_comment,
survey_email,

// Experiment event
experiment_name,
experiment_variation,
experiment_success
})
})
const data = response.ok ? await response.json() : {}
return data
}

export default async function initializeEvents () {
await sendEvent({ type: 'page' })
}
6 changes: 4 additions & 2 deletions javascripts/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ import localization from './localization'
import helpfulness from './helpfulness'
import experiment from './experiment'
import { fillCsrf } from './get-csrf'
import initializeEvents from './events'

document.addEventListener('DOMContentLoaded', () => {
document.addEventListener('DOMContentLoaded', async () => {
displayPlatformSpecificContent()
explorer()
search()
Expand All @@ -27,7 +28,8 @@ document.addEventListener('DOMContentLoaded', () => {
wrapCodeTerms()
print()
localization()
fillCsrf()
await fillCsrf() // this must complete before any POST calls
helpfulness()
experiment()
initializeEvents()
})
23 changes: 20 additions & 3 deletions lib/hydro.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
const crypto = require('crypto')
const fetch = require('node-fetch')

const SCHEMAS = {
page: 'docs.v0.PageEvent',
exit: 'docs.v0.ExitEvent',
link: 'docs.v0.LinkEvent',
search: 'docs.v0.SearchEvent',
navigate: 'docs.v0.NavigateEvent',
survey: 'docs.v0.SurveyEvent',
experiment: 'docs.v0.ExperimentEvent'
}

module.exports = class Hydro {
constructor ({ secret, endpoint }) {
constructor ({ secret, endpoint } = {}) {
this.secret = secret || process.env.HYDRO_SECRET
this.endpoint = endpoint || process.env.HYDRO_ENDPOINT
this.schemas = SCHEMAS
}

/**
Expand Down Expand Up @@ -32,7 +43,13 @@ module.exports = class Hydro {
* @param {[{ schema: string, value: any }]} events
*/
async publishMany (events) {
const body = JSON.stringify({ events })
const body = JSON.stringify({
events: events.map(({ schema, value }) => ({
schema,
value: JSON.stringify(value), // We must double-encode the value property
cluster: 'potomac' // We only have ability to publish externally to potomac cluster
}))
})
const token = this.generatePayloadHmac(body)

return fetch(this.endpoint, {
Expand All @@ -41,7 +58,7 @@ module.exports = class Hydro {
headers: {
Authorization: `Hydro ${token}`,
'Content-Type': 'application/json',
'X-Hydro-App': 'docs'
'X-Hydro-App': 'docs-production'
}
})
}
Expand Down
Loading