Skip to content

Commit

Permalink
Add analytics.settings api (#1090)
Browse files Browse the repository at this point in the history
  • Loading branch information
silesky authored May 22, 2024
1 parent 2299e9a commit b611746
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 44 deletions.
6 changes: 6 additions & 0 deletions .changeset/violet-actors-pump.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@segment/analytics-next': minor
---

- Add public settings API
- Do not expose loadLegacySettings / loadCDNSettings (private API)
47 changes: 37 additions & 10 deletions packages/browser/src/browser/__tests__/integration.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
/* eslint-disable @typescript-eslint/no-floating-promises */
import { cdnSettingsKitchenSink } from '../../test-helpers/fixtures/cdn-settings'
import {
cdnSettingsKitchenSink,
cdnSettingsMinimal,
} from '../../test-helpers/fixtures/cdn-settings'
import { createMockFetchImplementation } from '../../test-helpers/fixtures/create-fetch-method'
import { Context } from '../../core/context'
import { Plugin } from '../../core/plugin'
import { JSDOM } from 'jsdom'
import { Analytics, InitOptions } from '../../core/analytics'
import { LegacyDestination } from '../../plugins/ajs-destination'
import { PersistedPriorityQueue } from '../../lib/priority-queue/persisted'
// @ts-ignore loadLegacySettings mocked dependency is accused as unused
import { AnalyticsBrowser, loadLegacySettings } from '..'
// @ts-ignore loadCDNSettings mocked dependency is accused as unused
import { AnalyticsBrowser, loadCDNSettings } from '..'
// @ts-ignore isOffline mocked dependency is accused as unused
import { isOffline } from '../../core/connection'
import * as SegmentPlugin from '../../plugins/segmentio'
Expand All @@ -23,7 +26,8 @@ import {
highEntropyTestData,
lowEntropyTestData,
} from '../../test-helpers/fixtures/client-hints'
import { getGlobalAnalytics, NullAnalytics } from '../..'
import { getGlobalAnalytics } from '../../lib/global-analytics-helper'
import { NullAnalytics } from '../../core/analytics'
import { recordIntegrationMetric } from '../../core/stats/metric-helpers'

let fetchCalls: ReturnType<typeof parseFetchCall>[] = []
Expand Down Expand Up @@ -1030,24 +1034,47 @@ describe('use', () => {
})
})

describe('timeout', () => {
it('has a default timeout value', async () => {
describe('public settings api', () => {
it('has expected settings', async () => {
const [analytics] = await AnalyticsBrowser.load({
writeKey,
cdnSettings: cdnSettingsMinimal,
})

expect(analytics.settings).toEqual({
writeKey,
cdnSettings: cdnSettingsMinimal,
timeout: 300,
})
//@ts-ignore
expect(analytics.settings.timeout).toEqual(300)
})

it('should have a writeKey', async () => {
const [analytics] = await AnalyticsBrowser.load({
writeKey,
})

expect(analytics.settings.writeKey).toBe(writeKey)
})

it('should have cdn settings', async () => {
const [analytics] = await AnalyticsBrowser.load({
writeKey,
cdnSettings: cdnSettingsMinimal,
})

expect(analytics.settings.cdnSettings).toEqual(cdnSettingsMinimal)
})

it('can set a timeout value', async () => {
const [analytics] = await AnalyticsBrowser.load({
writeKey,
})
expect(analytics.settings.timeout).toEqual(300)
analytics.timeout(50)
//@ts-ignore
expect(analytics.settings.timeout).toEqual(50)
})
})

describe('register', () => {
it('will not invoke any plugins that have initialization errors', async () => {
const analytics = AnalyticsBrowser.load({
Expand Down Expand Up @@ -1190,7 +1217,7 @@ describe('retries', () => {

beforeEach(async () => {
// @ts-ignore ignore reassining function
loadLegacySettings = jest.fn().mockReturnValue(
loadCDNSettings = jest.fn().mockReturnValue(
Promise.resolve({
integrations: { 'Segment.io': { retryQueue: false } },
})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { JSDOM } from 'jsdom'
import { Analytics } from '../../core/analytics'
// @ts-ignore loadLegacySettings mocked dependency is accused as unused
// @ts-ignore loadCDNSettings mocked dependency is accused as unused
import { AnalyticsBrowser } from '..'
import { setGlobalCDNUrl } from '../../lib/parse-cdn'
import { TEST_WRITEKEY } from '../../test-helpers/test-writekeys'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import jsdom, { JSDOM } from 'jsdom'
import { InitOptions, getGlobalAnalytics } from '../../'
import { AnalyticsBrowser, loadLegacySettings } from '../../browser'
import { AnalyticsBrowser, loadCDNSettings } from '../../browser'
import { snippet } from '../../tester/__fixtures__/segment-snippet'
import { install } from '../standalone-analytics'
import unfetch from 'unfetch'
Expand Down Expand Up @@ -130,7 +130,7 @@ describe('standalone bundle', () => {
// @ts-ignore ignore Response required fields
.mockImplementation((): Promise<Response> => fetchSettings)

await loadLegacySettings(segmentDotCom)
await loadCDNSettings(segmentDotCom)

expect(unfetch).toHaveBeenCalledWith(
'https://cdn.foo.com/v1/projects/foo/settings'
Expand All @@ -145,7 +145,7 @@ describe('standalone bundle', () => {
const mockCdn = 'http://my-overridden-cdn.com'

getGlobalAnalytics()!._cdn = mockCdn
await loadLegacySettings(segmentDotCom)
await loadCDNSettings(segmentDotCom)

expect(unfetch).toHaveBeenCalledWith(expect.stringContaining(mockCdn))
})
Expand Down
38 changes: 21 additions & 17 deletions packages/browser/src/browser/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,7 @@ import { getProcessEnv } from '../lib/get-process-env'
import { getCDN, setGlobalCDNUrl } from '../lib/parse-cdn'

import { fetch } from '../lib/fetch'
import {
Analytics,
AnalyticsSettings,
NullAnalytics,
InitOptions,
} from '../core/analytics'
import { Analytics, NullAnalytics, InitOptions } from '../core/analytics'
import { Context } from '../core/context'
import { Plan } from '../core/events'
import { Plugin } from '../core/plugin'
Expand Down Expand Up @@ -111,7 +106,8 @@ export interface CDNSettings {
}
}

export interface AnalyticsBrowserSettings extends AnalyticsSettings {
export interface AnalyticsBrowserSettings {
writeKey: string
/**
* The settings for the Segment Source.
* If provided, `AnalyticsBrowser` will not fetch remote settings
Expand All @@ -122,9 +118,17 @@ export interface AnalyticsBrowserSettings extends AnalyticsSettings {
* If provided, will override the default Segment CDN (https://cdn.segment.com) for this application.
*/
cdnURL?: string
/**
* Plugins or npm-installed action destinations
*/
plugins?: (Plugin | PluginFactory)[]
/**
* npm-installed classic destinations
*/
classicIntegrations?: ClassicIntegrationSource[]
}

export function loadLegacySettings(
export function loadCDNSettings(
writeKey: string,
cdnURL?: string
): Promise<CDNSettings> {
Expand Down Expand Up @@ -330,31 +334,31 @@ async function loadAnalytics(
preInitBuffer.push(new PreInitMethodCall('page', []))
}

let legacySettings =
let cdnSettings =
settings.cdnSettings ??
(await loadLegacySettings(settings.writeKey, settings.cdnURL))
(await loadCDNSettings(settings.writeKey, settings.cdnURL))

if (options.updateCDNSettings) {
legacySettings = options.updateCDNSettings(legacySettings)
cdnSettings = options.updateCDNSettings(cdnSettings)
}

// if options.disable is a function, we allow user to disable analytics based on CDN Settings
if (typeof options.disable === 'function') {
const disabled = await options.disable(legacySettings)
const disabled = await options.disable(cdnSettings)
if (disabled) {
return [new NullAnalytics(), Context.system()]
}
}

const retryQueue: boolean =
legacySettings.integrations['Segment.io']?.retryQueue ?? true
cdnSettings.integrations['Segment.io']?.retryQueue ?? true

options = {
retryQueue,
...options,
}

const analytics = new Analytics(settings, options)
const analytics = new Analytics({ ...settings, cdnSettings }, options)

attachInspector(analytics)

Expand All @@ -367,8 +371,8 @@ async function loadAnalytics(
| undefined

Stats.initRemoteMetrics({
...legacySettings.metrics,
host: segmentLoadOptions?.apiHost ?? legacySettings.metrics?.host,
...cdnSettings.metrics,
host: segmentLoadOptions?.apiHost ?? cdnSettings.metrics?.host,
protocol: segmentLoadOptions?.protocol,
})

Expand All @@ -377,7 +381,7 @@ async function loadAnalytics(

const ctx = await registerPlugins(
settings.writeKey,
legacySettings,
cdnSettings,
analytics,
options,
plugins,
Expand Down
39 changes: 28 additions & 11 deletions packages/browser/src/core/analytics/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,7 @@ import { EventQueue } from '../queue/event-queue'
import { Group, ID, User, UserOptions } from '../user'
import autoBind from '../../lib/bind-all'
import { PersistedPriorityQueue } from '../../lib/priority-queue/persisted'
import type {
LegacyIntegration,
ClassicIntegrationSource,
} from '../../plugins/ajs-destination/types'
import type { LegacyIntegration } from '../../plugins/ajs-destination/types'
import type {
DestinationMiddlewareFunction,
MiddlewareFunction,
Expand All @@ -52,7 +49,6 @@ import {
initializeStorages,
isArrayOfStoreType,
} from '../storage'
import { PluginFactory } from '../../plugins/remote-loader'
import { setGlobalAnalytics } from '../../lib/global-analytics-helper'
import { popPageContext } from '../buffer'

Expand All @@ -75,11 +71,33 @@ function createDefaultQueue(
return new EventQueue(priorityQueue)
}

/**
* The public settings that are set on the analytics instance
*/
export class AnalyticsInstanceSettings {
readonly writeKey: string
/**
* This is an unstable API, it may change in the future without warning.
*/
readonly cdnSettings: CDNSettings

/**
* Auto-track specific timeout setting for legacy purposes.
*/
timeout = 300

constructor(settings: AnalyticsSettings) {
this.writeKey = settings.writeKey
this.cdnSettings = settings.cdnSettings ?? { integrations: {} }
}
}

/**
* The settings that are used to configure the analytics instance
*/
export interface AnalyticsSettings {
writeKey: string
timeout?: number
plugins?: (Plugin | PluginFactory)[]
classicIntegrations?: ClassicIntegrationSource[]
cdnSettings?: CDNSettings
}

export interface InitOptions {
Expand Down Expand Up @@ -155,7 +173,7 @@ export class Analytics
extends Emitter
implements AnalyticsCore, AnalyticsClassic
{
protected settings: AnalyticsSettings
settings: AnalyticsInstanceSettings
private _user: User
private _group: Group
private eventFactory: EventFactory
Expand All @@ -177,8 +195,7 @@ export class Analytics
super()
const cookieOptions = options?.cookie
const disablePersistance = options?.disableClientPersistence ?? false
this.settings = settings
this.settings.timeout = this.settings.timeout ?? 300
this.settings = new AnalyticsInstanceSettings(settings)
this.queue =
queue ??
createDefaultQueue(
Expand Down
9 changes: 7 additions & 2 deletions packages/browser/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
export * from './core/analytics'
export * from './browser'
export { Analytics, AnalyticsSettings, InitOptions } from './core/analytics'
export {
AnalyticsBrowser,
AnalyticsBrowserSettings,
CDNSettings,
RemoteIntegrationSettings,
} from './browser'
export * from './node'

export * from './core/context'
Expand Down

0 comments on commit b611746

Please sign in to comment.