Refactor analytics services to support multiple providers
The analytics manager has been significantly refactored to allow the use of multiple providers simultaneously. The API has been adjusted, replacing the single active service with a new Map called activeServices, to hold the active analytics services. Additionally, several methods have been modified to return promises rather than void. The 'defaultProvider' property has been removed, as it no longer fits into the system architecture.
This commit is contained in:
@@ -5,69 +5,69 @@ import type {
|
||||
CreateAnalyticsManagerOptions,
|
||||
} from './types';
|
||||
|
||||
/**
|
||||
* Creates an analytics manager that can be used to track page views and events. The manager is initialized with a
|
||||
* default provider and can be switched to a different provider at any time. The manager will use a NullAnalyticsService
|
||||
* if the provider is not registered.
|
||||
* @param options
|
||||
*/
|
||||
export function createAnalyticsManager<T extends string, Config extends object>(
|
||||
options: CreateAnalyticsManagerOptions<T, Config>,
|
||||
): AnalyticsManager {
|
||||
let activeService: AnalyticsService = NullAnalyticsService;
|
||||
const activeServices = new Map<T, AnalyticsService>();
|
||||
|
||||
const getActiveService = (): AnalyticsService => {
|
||||
if (activeService === NullAnalyticsService) {
|
||||
const getActiveServices = (): AnalyticsService[] => {
|
||||
if (activeServices.size === 0) {
|
||||
console.debug(
|
||||
'Analytics service not initialized. Using NullAnalyticsService.',
|
||||
);
|
||||
}
|
||||
|
||||
return activeService;
|
||||
};
|
||||
|
||||
const initialize = (provider: T, config: Config) => {
|
||||
const factory = options.providers[provider];
|
||||
|
||||
if (!factory) {
|
||||
console.warn(
|
||||
`Analytics provider '${provider}' not registered. Using NullAnalyticsService.`,
|
||||
'No active analytics services. Using NullAnalyticsService.',
|
||||
);
|
||||
|
||||
activeService = NullAnalyticsService;
|
||||
return;
|
||||
return [NullAnalyticsService];
|
||||
}
|
||||
|
||||
activeService = factory(config);
|
||||
activeService.initialize();
|
||||
return Array.from(activeServices.values());
|
||||
};
|
||||
|
||||
// Initialize with the default provider
|
||||
initialize(options.defaultProvider, {} as Config);
|
||||
|
||||
return {
|
||||
addProvider: (
|
||||
provider: T,
|
||||
config: Config,
|
||||
) => {
|
||||
const factory = options.providers[provider];
|
||||
|
||||
if (!factory) {
|
||||
console.warn(
|
||||
`Analytics provider '${provider}' not registered. Skipping initialization.`,
|
||||
);
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const service = factory(config);
|
||||
activeServices.set(provider, service);
|
||||
|
||||
return service.initialize();
|
||||
},
|
||||
|
||||
removeProvider: (provider: T) => {
|
||||
activeServices.delete(provider);
|
||||
},
|
||||
|
||||
identify: (userId: string, traits?: Record<string, string>) => {
|
||||
return getActiveService().identify(userId, traits);
|
||||
return Promise.all(
|
||||
getActiveServices().map((service) => service.identify(userId, traits)),
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Track a page view with the given URL.
|
||||
* @param url
|
||||
*/
|
||||
trackPageView: (url: string) => {
|
||||
return getActiveService().trackPageView(url);
|
||||
return Promise.all(
|
||||
getActiveServices().map((service) => service.trackPageView(url)),
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Track an event with the given name and properties.
|
||||
* @param eventName
|
||||
* @param eventProperties
|
||||
*/
|
||||
trackEvent: (
|
||||
eventName: string,
|
||||
eventProperties?: Record<string, string | string[]>,
|
||||
) => {
|
||||
return getActiveService().trackEvent(eventName, eventProperties);
|
||||
return Promise.all(
|
||||
getActiveServices().map((service) =>
|
||||
service.trackEvent(eventName, eventProperties),
|
||||
),
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ import { NullAnalyticsService } from './null-analytics-service';
|
||||
import type { AnalyticsManager } from './types';
|
||||
|
||||
export const analytics: AnalyticsManager = createAnalyticsManager({
|
||||
defaultProvider: 'null',
|
||||
providers: {
|
||||
null: () => NullAnalyticsService,
|
||||
},
|
||||
|
||||
@@ -3,7 +3,8 @@ import { AnalyticsService } from './types';
|
||||
const noop = (event: string) => {
|
||||
// do nothing - this is to prevent errors when the analytics service is not initialized
|
||||
|
||||
return (...args: unknown[]) => {
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
return async (...args: unknown[]) => {
|
||||
console.debug(
|
||||
`Noop analytics service called with event: ${event}`,
|
||||
...args.filter(Boolean),
|
||||
|
||||
@@ -2,19 +2,25 @@ interface TrackEvent {
|
||||
trackEvent(
|
||||
eventName: string,
|
||||
eventProperties?: Record<string, string | string[]>,
|
||||
): void;
|
||||
): Promise<unknown>;
|
||||
}
|
||||
|
||||
interface TrackPageView {
|
||||
trackPageView(url: string): void;
|
||||
trackPageView(url: string): Promise<unknown>;
|
||||
}
|
||||
|
||||
interface Identify {
|
||||
identify(userId: string, traits?: Record<string, string>): void;
|
||||
identify(userId: string, traits?: Record<string, string>): Promise<unknown>;
|
||||
}
|
||||
|
||||
interface ProviderManager {
|
||||
addProvider(provider: string, config: object): Promise<unknown>;
|
||||
|
||||
removeProvider(provider: string): void;
|
||||
}
|
||||
|
||||
export interface AnalyticsService extends TrackPageView, TrackEvent, Identify {
|
||||
initialize(): void;
|
||||
initialize(): Promise<unknown>;
|
||||
}
|
||||
|
||||
export type AnalyticsProviderFactory<Config extends object> = (
|
||||
@@ -25,8 +31,11 @@ export interface CreateAnalyticsManagerOptions<
|
||||
T extends string,
|
||||
Config extends object,
|
||||
> {
|
||||
defaultProvider: T;
|
||||
providers: Record<T, AnalyticsProviderFactory<Config>>;
|
||||
}
|
||||
|
||||
export interface AnalyticsManager extends TrackPageView, TrackEvent, Identify {}
|
||||
export interface AnalyticsManager
|
||||
extends TrackPageView,
|
||||
TrackEvent,
|
||||
Identify,
|
||||
ProviderManager {}
|
||||
|
||||
Reference in New Issue
Block a user