diff --git a/packages/rum-core/src/domain/configuration/remoteConfiguration.spec.ts b/packages/rum-core/src/domain/configuration/remoteConfiguration.spec.ts index e69006fc8a..16bd86e3af 100644 --- a/packages/rum-core/src/domain/configuration/remoteConfiguration.spec.ts +++ b/packages/rum-core/src/domain/configuration/remoteConfiguration.spec.ts @@ -478,6 +478,46 @@ describe('remoteConfiguration', () => { }) }) + describe('localStorage strategy', () => { + const LOCAL_STORAGE_KEY = 'dd_test_version' + + beforeEach(() => { + localStorage.setItem(LOCAL_STORAGE_KEY, 'my-version') + registerCleanupTask(() => localStorage.removeItem(LOCAL_STORAGE_KEY)) + }) + + it('should resolve a configuration value from localStorage', () => { + expectAppliedRemoteConfigurationToBe( + { version: { rcSerializedType: 'dynamic', strategy: 'localStorage', key: LOCAL_STORAGE_KEY } }, + { version: 'my-version' } + ) + expect(metrics.get().localStorage).toEqual({ success: 1 }) + }) + + it('should resolve a configuration value from localStorage with an extractor', () => { + localStorage.setItem(LOCAL_STORAGE_KEY, 'version-123') + expectAppliedRemoteConfigurationToBe( + { + version: { + rcSerializedType: 'dynamic', + strategy: 'localStorage', + key: LOCAL_STORAGE_KEY, + extractor: { rcSerializedType: 'regex', value: '\\d+' }, + }, + }, + { version: '123' } + ) + }) + + it('should resolve to undefined if the key is missing', () => { + expectAppliedRemoteConfigurationToBe( + { version: { rcSerializedType: 'dynamic', strategy: 'localStorage', key: 'non_existent_key' } }, + { version: undefined } + ) + expect(metrics.get().localStorage).toEqual({ missing: 1 }) + }) + }) + describe('with extractor', () => { beforeEach(() => { setCookie(COOKIE_NAME, 'my-version-123', ONE_MINUTE) diff --git a/packages/rum-core/src/domain/configuration/remoteConfiguration.ts b/packages/rum-core/src/domain/configuration/remoteConfiguration.ts index 217ecfb2b0..79e3850646 100644 --- a/packages/rum-core/src/domain/configuration/remoteConfiguration.ts +++ b/packages/rum-core/src/domain/configuration/remoteConfiguration.ts @@ -44,6 +44,7 @@ export interface RemoteConfigurationMetrics extends Context { cookie?: RemoteConfigurationMetricCounters dom?: RemoteConfigurationMetricCounters js?: RemoteConfigurationMetricCounters + localStorage?: RemoteConfigurationMetricCounters } interface RemoteConfigurationMetricCounters { @@ -147,6 +148,9 @@ export function applyRemoteConfiguration( case 'js': resolvedValue = resolveJsValue(property) break + case 'localStorage': + resolvedValue = resolveLocalStorageValue(property) + break default: display.error(`Unsupported remote configuration: "strategy": "${strategy as string}"`) return @@ -164,6 +168,18 @@ export function applyRemoteConfiguration( return value } + function resolveLocalStorageValue({ key }: { key: string }) { + let value: string | null + try { + value = localStorage.getItem(key) + } catch { + metrics.increment('localStorage', 'failure') + return + } + metrics.increment('localStorage', value !== null ? 'success' : 'missing') + return value ?? undefined + } + function resolveDomValue({ selector, attribute }: { selector: string; attribute?: string }) { let element: Element | null try { diff --git a/packages/rum-core/src/domain/configuration/remoteConfiguration.types.ts b/packages/rum-core/src/domain/configuration/remoteConfiguration.types.ts index 176d25d6fd..64851de309 100644 --- a/packages/rum-core/src/domain/configuration/remoteConfiguration.types.ts +++ b/packages/rum-core/src/domain/configuration/remoteConfiguration.types.ts @@ -26,6 +26,13 @@ export type DynamicOption = extractor?: SerializedRegex [k: string]: unknown } + | { + rcSerializedType: 'dynamic' + strategy: 'localStorage' + key: string + extractor?: SerializedRegex + [k: string]: unknown + } /** * RUM Browser & Mobile SDKs Remote Configuration properties @@ -73,6 +80,13 @@ export interface RumSdkConfig { extractor?: SerializedRegex [k: string]: unknown } + | { + rcSerializedType: 'dynamic' + strategy: 'localStorage' + key: string + extractor?: SerializedRegex + [k: string]: unknown + } /** * The percentage of sessions tracked */