-
Notifications
You must be signed in to change notification settings - Fork 2.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add map.load telemetry event #7431
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,7 +12,7 @@ import type { RequestParameters } from './ajax'; | |
import type { Cancelable } from '../types/cancelable'; | ||
|
||
const help = 'See https://www.mapbox.com/api-documentation/#access-tokens'; | ||
const turnstileEventStorageKey = 'mapbox.turnstileEventData'; | ||
const telemEventKey = 'mapbox.eventData'; | ||
|
||
type UrlObject = {| | ||
protocol: string, | ||
|
@@ -133,19 +133,122 @@ function formatUrl(obj: UrlObject): string { | |
return `${obj.protocol}://${obj.authority}${obj.path}${params}`; | ||
} | ||
|
||
export class TurnstileEvent { | ||
class TelemetryEvent { | ||
eventData: { anonId: ?string, lastSuccess: ?number, accessToken: ?string}; | ||
queue: Array<number>; | ||
pending: boolean | ||
queue: Array<any>; | ||
pendingRequest: ?Cancelable; | ||
|
||
constructor() { | ||
this.eventData = { anonId: null, lastSuccess: null, accessToken: config.ACCESS_TOKEN}; | ||
this.queue = []; | ||
this.pending = false; | ||
this.pendingRequest = null; | ||
} | ||
|
||
fetchEventData() { | ||
const isLocalStorageAvailable = storageAvailable('localStorage'); | ||
const storageKey = `${telemEventKey}:${config.ACCESS_TOKEN || ''}`; | ||
|
||
if (isLocalStorageAvailable) { | ||
//Retrieve cached data | ||
try { | ||
const data = window.localStorage.getItem(storageKey); | ||
if (data) { | ||
this.eventData = JSON.parse(data); | ||
} | ||
} catch (e) { | ||
warnOnce('Unable to read from LocalStorage'); | ||
} | ||
} | ||
} | ||
|
||
saveEventData() { | ||
const isLocalStorageAvailable = storageAvailable('localStorage'); | ||
const storageKey = `${telemEventKey}:${config.ACCESS_TOKEN || ''}`; | ||
|
||
if (isLocalStorageAvailable) { | ||
try { | ||
window.localStorage.setItem(storageKey, JSON.stringify(this.eventData)); | ||
} catch (e) { | ||
warnOnce('Unable to write to LocalStorage'); | ||
} | ||
} | ||
|
||
} | ||
|
||
processRequests() {} | ||
|
||
queueRequest(date: number | {id: number, timestamp: number}) { | ||
this.queue.push(date); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. mmm I don't think that's true. date is one argument, for turnstile events it is just a number, for map load events it is the object w id and timestamp. its working correctly for me locally and is also verified in the tests 🕵️♀️ |
||
this.processRequests(); | ||
} | ||
} | ||
|
||
export class MapLoadEvent extends TelemetryEvent { | ||
eventData: { anonId: ?string, lastSuccess: ?number, accessToken: ?string}; | ||
queue: Array<{ id: number, timestamp: number}>; | ||
pendingRequest: ?Cancelable; | ||
+success: {[number]: boolean}; | ||
|
||
constructor() { | ||
super(); | ||
this.success = {}; | ||
} | ||
|
||
postMapLoadEvent(tileUrls: Array<string>, mapId: number) { | ||
//Enabled only when Mapbox Access Token is set and a source uses | ||
// mapbox tiles. | ||
if (config.ACCESS_TOKEN && | ||
Array.isArray(tileUrls) && | ||
tileUrls.some(url => isMapboxHTTPURL(url))) { | ||
this.queueRequest({id: mapId, timestamp: Date.now()}); | ||
} | ||
} | ||
|
||
processRequests() { | ||
if (this.pendingRequest || this.queue.length === 0) return; | ||
const {id, timestamp} = this.queue.shift(); | ||
|
||
// Only one load event should fire per map | ||
if (id && this.success[id]) return; | ||
|
||
if (!this.eventData.anonId) { | ||
this.fetchEventData(); | ||
} | ||
|
||
if (!validateUuid(this.eventData.anonId)) { | ||
this.eventData.anonId = uuid(); | ||
} | ||
|
||
const eventsUrlObject: UrlObject = parseUrl(config.EVENTS_URL); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Code to send the request is common for all telemetry events, maybe it could be pulled into
It would also ensure that all events correctly save event data and process the queue |
||
eventsUrlObject.params.push(`access_token=${config.ACCESS_TOKEN || ''}`); | ||
const request: RequestParameters = { | ||
url: formatUrl(eventsUrlObject), | ||
headers: { | ||
'Content-Type': 'text/plain' //Skip the pre-flight OPTIONS request | ||
}, | ||
body: JSON.stringify([{ | ||
event: 'map.load', | ||
created: new Date(timestamp).toISOString(), | ||
sdkIdentifier: 'mapbox-gl-js', | ||
sdkVersion: version, | ||
userId: this.eventData.anonId | ||
}]) | ||
}; | ||
|
||
this.pendingRequest = postData(request, (error) => { | ||
this.pendingRequest = null; | ||
if (!error) { | ||
if (id) this.success[id] = true; | ||
this.saveEventData(); | ||
this.processRequests(); | ||
} | ||
}); | ||
} | ||
} | ||
|
||
|
||
export class TurnstileEvent extends TelemetryEvent { | ||
|
||
postTurnstileEvent(tileUrls: Array<string>) { | ||
//Enabled only when Mapbox Access Token is set and a source uses | ||
// mapbox tiles. | ||
|
@@ -156,34 +259,20 @@ export class TurnstileEvent { | |
} | ||
} | ||
|
||
queueRequest(date: number) { | ||
this.queue.push(date); | ||
this.processRequests(); | ||
} | ||
|
||
processRequests() { | ||
if (this.pendingRequest || this.queue.length === 0) { | ||
return; | ||
} | ||
const storageKey = `${turnstileEventStorageKey}:${config.ACCESS_TOKEN || ''}`; | ||
const isLocalStorageAvailable = storageAvailable('localStorage'); | ||
let dueForEvent = this.eventData.accessToken ? (this.eventData.accessToken !== config.ACCESS_TOKEN) : false; | ||
|
||
let dueForEvent = this.eventData.accessToken ? (this.eventData.accessToken !== config.ACCESS_TOKEN) : false; | ||
//Reset event data cache if the access token changed. | ||
if (dueForEvent) { | ||
this.eventData.anonId = this.eventData.lastSuccess = null; | ||
} | ||
if ((!this.eventData.anonId || !this.eventData.lastSuccess) && | ||
isLocalStorageAvailable) { | ||
if (!this.eventData.anonId || !this.eventData.lastSuccess) { | ||
//Retrieve cached data | ||
try { | ||
const data = window.localStorage.getItem(storageKey); | ||
if (data) { | ||
this.eventData = JSON.parse(data); | ||
} | ||
} catch (e) { | ||
warnOnce('Unable to read from LocalStorage'); | ||
} | ||
this.fetchEventData(); | ||
} | ||
|
||
if (!validateUuid(this.eventData.anonId)) { | ||
|
@@ -227,19 +316,15 @@ export class TurnstileEvent { | |
if (!error) { | ||
this.eventData.lastSuccess = nextUpdate; | ||
this.eventData.accessToken = config.ACCESS_TOKEN; | ||
if (isLocalStorageAvailable) { | ||
try { | ||
window.localStorage.setItem(storageKey, JSON.stringify(this.eventData)); | ||
} catch (e) { | ||
warnOnce('Unable to write to LocalStorage'); | ||
} | ||
} | ||
this.saveEventData(); | ||
this.processRequests(); | ||
} | ||
}); | ||
} | ||
} | ||
|
||
const turnstileEvent_ = new TurnstileEvent(); | ||
|
||
export const postTurnstileEvent = turnstileEvent_.postTurnstileEvent.bind(turnstileEvent_); | ||
|
||
const mapLoadEvent_ = new MapLoadEvent(); | ||
export const postMapLoadEvent = mapLoadEvent_.postMapLoadEvent.bind(mapLoadEvent_); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would override any previously stored event data. In the case of a turnstile request finishing before the map load request, the
lastSuccess
property of the turnstile event would be removed, and more than one turnstile request may be processed.The event data could include separate objects for each event type or use a different key for each event type.