Chore: Rudderstack upgrade to SDK v3 behind flag (#114126)

* add rudderstack version upgrade feature flag

* add new rudderstack backend file
gate new backend with feature flag

* fix global type error
This commit is contained in:
Luminessa Starlight
2025-11-19 11:48:23 -05:00
committed by GitHub
parent 1f9ed37b1b
commit 3e7b952488
8 changed files with 222 additions and 2 deletions
+5
View File
@@ -1188,4 +1188,9 @@ export interface FeatureToggles {
* Enable TTL plugin instance manager
*/
ttlPluginInstanceManager?: boolean;
/**
* Enables the new version of rudderstack
* @default false
*/
rudderstackUpgrade?: boolean;
}
+12
View File
@@ -2064,6 +2064,18 @@ var (
FrontendOnly: true,
Owner: grafanaPluginsPlatformSquad,
},
{
Name: "rudderstackUpgrade",
Description: "Enables the new version of rudderstack",
FrontendOnly: true,
Stage: FeatureStageExperimental,
Owner: grafanaFrontendPlatformSquad,
Expression: "false",
RequiresRestart: false,
AllowSelfServe: false,
HideFromDocs: false,
HideFromAdminPage: false,
},
}
)
+1
View File
@@ -265,3 +265,4 @@ kubernetesAnnotations,experimental,@grafana/grafana-backend-services-squad,false
awsDatasourcesHttpProxy,experimental,@grafana/aws-datasources,false,false,false
transformationsEmptyPlaceholder,preview,@grafana/datapro,false,false,true
ttlPluginInstanceManager,experimental,@grafana/plugins-platform-backend,false,false,true
rudderstackUpgrade,experimental,@grafana/grafana-frontend-platform,false,false,true
1 Name Stage Owner requiresDevMode RequiresRestart FrontendOnly
265 awsDatasourcesHttpProxy experimental @grafana/aws-datasources false false false
266 transformationsEmptyPlaceholder preview @grafana/datapro false false true
267 ttlPluginInstanceManager experimental @grafana/plugins-platform-backend false false true
268 rudderstackUpgrade experimental @grafana/grafana-frontend-platform false false true
+4
View File
@@ -1069,4 +1069,8 @@ const (
// FlagTtlPluginInstanceManager
// Enable TTL plugin instance manager
FlagTtlPluginInstanceManager = "ttlPluginInstanceManager"
// FlagRudderstackUpgrade
// Enables the new version of rudderstack
FlagRudderstackUpgrade = "rudderstackUpgrade"
)
+14
View File
@@ -3662,6 +3662,20 @@
"codeowner": "@grafana/identity-access-team"
}
},
{
"metadata": {
"name": "rudderstackUpgrade",
"resourceVersion": "1763481217872",
"creationTimestamp": "2025-11-18T15:53:37Z"
},
"spec": {
"description": "Enables the new version of rudderstack",
"stage": "experimental",
"codeowner": "@grafana/grafana-frontend-platform",
"frontend": true,
"expression": "false"
}
},
{
"metadata": {
"name": "savedQueries",
@@ -21,7 +21,21 @@ interface RudderstackAPIOptions {
interface Rudderstack {
identify: (identifier: string, traits: Properties, options?: RudderstackAPIOptions) => void;
load: (writeKey: string, dataPlaneURL: string, options: { configUrl?: string; destSDKBaseURL?: string }) => void;
// load type set to match Rudderstack v3, for global type compatibility with new version.
load: (
writeKey: string,
dataPlaneURL: string,
options: {
configUrl?: string;
destSDKBaseURL?: string;
storage?: {
encryption?: {
version: 'V3' | 'legacy';
};
migrate?: boolean;
};
}
) => void;
page: () => void;
track: (eventName: string, properties?: Properties) => void;
}
@@ -0,0 +1,166 @@
import { BuildInfo } from '@grafana/data';
import {
EchoBackend,
EchoEventType,
isExperimentViewEvent,
isInteractionEvent,
isPageviewEvent,
PageviewEchoEvent,
} from '@grafana/runtime';
import { User } from '../../../context_srv';
import { loadScript } from '../../utils';
type Properties = Record<string, string | boolean | number>;
interface RudderstackAPIOptions {
Intercom?: {
user_hash: string;
};
}
interface Rudderstack {
identify: (identifier: string, traits: Properties, options?: RudderstackAPIOptions) => void;
load: (
writeKey: string,
dataPlaneURL: string,
options: {
configUrl?: string;
destSDKBaseURL?: string;
storage?: {
encryption?: {
version: 'V3' | 'legacy';
};
migrate?: boolean;
};
}
) => void;
page: () => void;
track: (eventName: string, properties?: Properties) => void;
}
declare global {
interface Window {
// We say all methods are undefined because we can't be sure they're there
// and we should be extra cautious
rudderanalytics?: Partial<Rudderstack>;
}
}
export interface RudderstackBackendOptions {
writeKey: string;
dataPlaneUrl: string;
buildInfo: BuildInfo;
user?: User;
sdkUrl?: string;
configUrl?: string;
integrationsUrl?: string;
}
export class RudderstackBackend implements EchoBackend<PageviewEchoEvent, RudderstackBackendOptions> {
supportedEvents = [EchoEventType.Pageview, EchoEventType.Interaction, EchoEventType.ExperimentView];
constructor(public options: RudderstackBackendOptions) {
const url = options.sdkUrl || `https://cdn.rudderlabs.com/v3/modern/rsa.min.js`;
loadScript(url);
// this preloadRudderstack functionality is mirror the loading snippet reference in the Rudderstack
// docs here: https://www.rudderstack.com/docs/sources/event-streams/sdks/rudderstack-javascript-sdk/installation/#using-cdn
// We don't need a bunch of what's in the snippet, and we can rely on browser support being modern,
// so this is much more terse and only needs to handle the temp setup that pushes calls to the
// rudderstack API into the temp array.
const preloadRudderstack: unknown[] = [];
const methodNames = [
'setDefaultInstanceKey',
'load',
'ready',
'page',
'track',
'identify',
'alias',
'group',
'reset',
'setAnonymousId',
'startSession',
'endSession',
'consent',
'addCustomIntegration',
];
methodNames.forEach((methodName) => {
// using Object.assign gets around the types being "wrong".
Object.assign(preloadRudderstack, {
[methodName]: function () {
preloadRudderstack.push([methodName].concat(Array.prototype.slice.call(arguments)));
},
});
});
Object.assign(window, { rudderanalytics: preloadRudderstack });
window.rudderanalytics?.load?.(options.writeKey, options.dataPlaneUrl, {
configUrl: options.configUrl,
destSDKBaseURL: options.integrationsUrl,
// these storage settings allow backwards compat across subdomains with the older rudderstack
// version. This would have to be changed to the new setting across all instances at once,
// and wouldn't be able to be rolled back.
storage: {
encryption: {
version: 'legacy',
},
migrate: false,
},
});
if (options.user) {
const { identifier, intercomIdentifier } = options.user.analytics;
const apiOptions: RudderstackAPIOptions = {};
if (intercomIdentifier) {
apiOptions.Intercom = {
user_hash: intercomIdentifier,
};
}
window.rudderanalytics?.identify?.(
identifier,
{
email: options.user.email,
orgId: options.user.orgId,
language: options.user.language,
version: options.buildInfo.version,
edition: options.buildInfo.edition,
},
apiOptions
);
}
window.rudderanalytics?.page?.();
}
addEvent = (e: PageviewEchoEvent) => {
if (!window.rudderanalytics) {
return;
}
if (isPageviewEvent(e)) {
window.rudderanalytics.page?.();
}
if (isInteractionEvent(e)) {
window.rudderanalytics.track?.(e.payload.interactionName, e.payload.properties);
}
if (isExperimentViewEvent(e)) {
window.rudderanalytics.track?.('experiment_viewed', {
experiment_id: e.payload.experimentId,
experiment_group: e.payload.experimentGroup,
experiment_variant: e.payload.experimentVariant,
});
}
};
// Not using Echo buffering, addEvent above sends events to GA as soon as they appear
flush = () => {};
}
+5 -1
View File
@@ -146,7 +146,11 @@ async function initRudderstackBackend() {
return;
}
const { RudderstackBackend } = await import('./backends/analytics/RudderstackBackend');
const modulePromise = config.featureToggles.rudderstackUpgrade
? import('./backends/analytics/RudderstackV3Backend')
: import('./backends/analytics/RudderstackBackend');
const { RudderstackBackend } = await modulePromise;
registerEchoBackend(
new RudderstackBackend({
writeKey: config.rudderstackWriteKey,