Skip to content

Commit 4ae8261

Browse files
Add setSdkInfo for wrapper SDK telemetry
GitOrigin-RevId: 10b2a59f1235a180d6ce7bb750414a222b6dab52
1 parent b02853e commit 4ae8261

4 files changed

Lines changed: 63 additions & 2 deletions

File tree

src/index.esm.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ export {default as MercatorCoordinate} from './geo/mercator_coordinate';
164164
export {Evented} from './util/evented';
165165
export {FreeCameraOptions} from './ui/free_camera';
166166
export {setRTLTextPlugin, getRTLTextPluginStatus} from './source/rtl_text_plugin';
167+
export {setSdkInfo} from './util/mapbox';
167168
export {addTileProvider} from './source/tile_provider';
168169
export {prewarm, clearPrewarmedResources} from './util/worker_pool_factory';
169170
export {setWorkerUrl} from './util/worker_class';

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import config, {setAccessToken, setBaseApiUrl, setMaxParallelImageRequests, getD
2020
import {Debug} from './util/debug';
2121
import {isSafari} from './util/util';
2222
import {setRTLTextPlugin, getRTLTextPluginStatus} from './source/rtl_text_plugin';
23+
import {setSdkInfo} from './util/mapbox';
2324
import {addTileProvider} from './source/tile_provider';
2425
import {getWorkerCount, setWorkerCount} from './util/worker_pool';
2526
import WorkerClass from './util/worker_class';
@@ -88,6 +89,7 @@ const exported = {
8889
supported,
8990
setRTLTextPlugin,
9091
getRTLTextPluginStatus,
92+
setSdkInfo,
9193
addTileProvider,
9294
Map,
9395
NavigationControl,

src/util/mapbox.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,28 @@ function isTelemetryEnabled(customAccessToken?: string | null): boolean {
322322
return true;
323323
}
324324

325+
let sdkInfo: string | undefined;
326+
327+
const SDK_INFO_RE = /^[\w.+-]+(\/[\w.+-]+)?$/;
328+
329+
const EVENT_SCHEMA_VERSION = '2.2';
330+
331+
/**
332+
* Internal API used by Mapbox wrapper SDKs (e.g. the Flutter or React Native bridges) to
333+
* self-identify in telemetry. Not part of the public API and subject to change. Pass a
334+
* `Name/version` string (e.g. `'FlutterPlugin/3.0.0'`); call once at startup before any maps
335+
* are created, as events from earlier maps won't carry it.
336+
*
337+
* @private
338+
*/
339+
export function setSdkInfo(info: string) {
340+
if (typeof info !== 'string' || info.length > 64 || !SDK_INFO_RE.test(info)) {
341+
warnOnce(`Invalid SDK info "${info}"; expected a "Name/version" string. Ignoring.`);
342+
return;
343+
}
344+
sdkInfo = info;
345+
}
346+
325347
type TelemetryEventType = 'appUserTurnstile' | 'map.load' | 'map.auth' | 'gljs.performance' | 'style.load' | 'metrics';
326348

327349
export class TelemetryEvent {
@@ -543,14 +565,17 @@ export class MapLoadEvent extends TelemetryEvent {
543565
this.refreshUUID();
544566
}
545567

546-
const additionalPayload = {
568+
const additionalPayload: Record<string, unknown> = {
569+
version: EVENT_SCHEMA_VERSION,
547570
sdkIdentifier: 'mapbox-gl-js',
548571
sdkVersion,
549572
skuId: SKU_ID,
550573
skuToken: this.skuToken,
551574
userId: this.anonId
552575
};
553576

577+
if (sdkInfo) additionalPayload.sdkInfo = sdkInfo;
578+
554579
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
555580
this.postEvent(timestamp, additionalPayload, (err) => {
556581
if (err) {
@@ -832,14 +857,17 @@ export class TurnstileEvent extends TelemetryEvent {
832857
return;
833858
}
834859

835-
const additionalPayload = {
860+
const additionalPayload: Record<string, unknown> = {
861+
version: EVENT_SCHEMA_VERSION,
836862
sdkIdentifier: 'mapbox-gl-js',
837863
sdkVersion,
838864
skuId: SKU_ID,
839865
"enabled.telemetry": false,
840866
userId: this.anonId
841867
};
842868

869+
if (sdkInfo) additionalPayload.sdkInfo = sdkInfo;
870+
843871
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
844872
this.postEvent(nextUpdate, additionalPayload, (err) => {
845873
if (!err) {

test/unit/util/mapbox.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -778,6 +778,8 @@ describe("mapbox", () => {
778778
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
779779
expect(mapLoadEvent["enabled.telemetry"]).toEqual(false);
780780
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
781+
expect(mapLoadEvent.version).toEqual('2.2');
782+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
781783
expect(!!mapLoadEvent.userId).toBeTruthy();
782784
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
783785
expect(!!mapLoadEvent.created).toBeTruthy();
@@ -1205,6 +1207,34 @@ describe("mapbox", () => {
12051207
expect(!!mapLoadEvent.userId).toBeTruthy();
12061208
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
12071209
expect(!!mapLoadEvent.created).toBeTruthy();
1210+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
1211+
expect(mapLoadEvent.version).toEqual('2.2');
1212+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
1213+
expect(mapLoadEvent.sdkInfo).toBeUndefined();
1214+
});
1215+
1216+
test('setSdkInfo ignores invalid values', async () => {
1217+
mapbox.setSdkInfo('not a valid value!');
1218+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
1219+
event.postMapLoadEvent(1, skuToken);
1220+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
1221+
const reqBody = await window.server.requests[0].requestBody;
1222+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
1223+
const mapLoadEvent = JSON.parse(reqBody.slice(1, reqBody.length - 1));
1224+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
1225+
expect(mapLoadEvent.sdkInfo).toBeUndefined();
1226+
});
1227+
1228+
test('includes sdkInfo when set via setSdkInfo', async () => {
1229+
mapbox.setSdkInfo('FlutterPlugin/3.0.0-beta.1');
1230+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
1231+
event.postMapLoadEvent(1, skuToken);
1232+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
1233+
const reqBody = await window.server.requests[0].requestBody;
1234+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
1235+
const mapLoadEvent = JSON.parse(reqBody.slice(1, reqBody.length - 1));
1236+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
1237+
expect(mapLoadEvent.sdkInfo).toEqual('FlutterPlugin/3.0.0-beta.1');
12081238
});
12091239

12101240
test('does not POST when mapboxgl.ACCESS_TOKEN is not set', () => {

0 commit comments

Comments
 (0)