From 3a3627c78cf9fb82da88c38a4ebf3fdbc28ae63a Mon Sep 17 00:00:00 2001 From: "Heiko W. Rupp" Date: Fri, 14 Mar 2025 10:13:15 +0100 Subject: [PATCH 1/7] Allow to tune down the logging on console. --- .../chatbot/examples/Analytics/Analytics.md | 3 +++ .../chatbot/examples/demos/Chatbot.tsx | 1 + .../src/tracking/console_tracking_provider.ts | 25 ++++++++++++------ .../src/tracking/posthog_tracking_provider.ts | 25 ++++++++++++------ .../src/tracking/segment_tracking_provider.ts | 25 ++++++++++++------ packages/module/src/tracking/tracking_spi.ts | 8 ++++-- .../src/tracking/umami_tracking_provider.ts | 26 +++++++++++++------ 7 files changed, 79 insertions(+), 34 deletions(-) diff --git a/packages/module/patternfly-docs/content/extensions/chatbot/examples/Analytics/Analytics.md b/packages/module/patternfly-docs/content/extensions/chatbot/examples/Analytics/Analytics.md index aa2dcd66a..1efb79613 100644 --- a/packages/module/patternfly-docs/content/extensions/chatbot/examples/Analytics/Analytics.md +++ b/packages/module/patternfly-docs/content/extensions/chatbot/examples/Analytics/Analytics.md @@ -40,6 +40,7 @@ Note that user code only interacts with: ```nolive const initProps: InitProps = { + verbose: false, segmentKey: 'TODO-key', // TODO add your key here // segmentCdn: 'https://my.org/cdn', // Set up segment cdn (optional) // segmentIntegrations: { // Provide Segment integrations (optional) @@ -57,6 +58,8 @@ const initProps: InitProps = { }; ``` +Note, that there is also a key `verbose` that allows you to enable debugging output on web-browser console. By default, this is set to `false`. + 1. Once this is done, you can create an instance of the `trackingAPI` and start sending events. ```nolive diff --git a/packages/module/patternfly-docs/content/extensions/chatbot/examples/demos/Chatbot.tsx b/packages/module/patternfly-docs/content/extensions/chatbot/examples/demos/Chatbot.tsx index dd4133296..bd54b49fa 100644 --- a/packages/module/patternfly-docs/content/extensions/chatbot/examples/demos/Chatbot.tsx +++ b/packages/module/patternfly-docs/content/extensions/chatbot/examples/demos/Chatbot.tsx @@ -98,6 +98,7 @@ export default MessageLoading; const date = new Date(); const initProps: InitProps = { + verbose: false, segmentKey: 'TODO-key', // TODO add your key here posthogKey: 'TODO-key', umamiKey: 'TODO-key', diff --git a/packages/module/src/tracking/console_tracking_provider.ts b/packages/module/src/tracking/console_tracking_provider.ts index 178b25b78..4bbf266a5 100644 --- a/packages/module/src/tracking/console_tracking_provider.ts +++ b/packages/module/src/tracking/console_tracking_provider.ts @@ -2,26 +2,35 @@ import { TrackingSpi } from './tracking_spi'; import { TrackingApi, TrackingEventProperties } from './tracking_api'; export class ConsoleTrackingProvider implements TrackingSpi, TrackingApi { + private verbose = false; trackPageView(url: string | undefined) { - // eslint-disable-next-line no-console - console.log('ConsoleProvider pageView', url); + if (this.verbose) { + // eslint-disable-next-line no-console + console.log('ConsoleProvider pageView ', url); + } } // eslint-disable-next-line @typescript-eslint/no-empty-function registerProvider(): void {} initialize(): void { - // eslint-disable-next-line no-console - console.log('ConsoleProvider initialize'); + if (this.verbose) { + // eslint-disable-next-line no-console + console.log('ConsoleProvider initialize'); + } } identify(userID: string): void { - // eslint-disable-next-line no-console - console.log('ConsoleProvider identify', userID); + if (this.verbose) { + // eslint-disable-next-line no-console + console.log('ConsoleProvider identify ', userID); + } } trackSingleItem(item: string, properties?: TrackingEventProperties): void { - // eslint-disable-next-line no-console - console.log('ConsoleProvider: ' + item, properties); + if (this.verbose) { + // eslint-disable-next-line no-console + console.log('ConsoleProvider: ' + item, properties); + } } getKey(): string { diff --git a/packages/module/src/tracking/posthog_tracking_provider.ts b/packages/module/src/tracking/posthog_tracking_provider.ts index fb8c7b934..19890a975 100644 --- a/packages/module/src/tracking/posthog_tracking_provider.ts +++ b/packages/module/src/tracking/posthog_tracking_provider.ts @@ -4,13 +4,16 @@ import { TrackingApi, TrackingEventProperties } from './tracking_api'; import { InitProps, TrackingSpi } from './tracking_spi'; export class PosthogTrackingProvider implements TrackingSpi, TrackingApi { + private verbose = false; getKey(): string { return 'posthogKey'; } initialize(props: InitProps): void { - // eslint-disable-next-line no-console - console.log('PosthogProvider initialize'); + if (this.verbose) { + // eslint-disable-next-line no-console + console.log('PosthogProvider initialize'); + } const posthogKey = props.posthogKey as string; posthog.init(posthogKey, { @@ -22,21 +25,27 @@ export class PosthogTrackingProvider implements TrackingSpi, TrackingApi { } identify(userID: string): void { - // eslint-disable-next-line no-console - console.log('PosthogProvider userID: ' + userID); + if (this.verbose) { + // eslint-disable-next-line no-console + console.log('PosthogProvider userID: ' + userID); + } posthog.identify(userID); } trackPageView(url: string | undefined): void { - // eslint-disable-next-line no-console - console.log('PostHogProvider url', url); + if (this.verbose) { + // eslint-disable-next-line no-console + console.log('PostHogProvider url', url); + } // TODO posthog seems to record that automatically. // How to not clash with this here? Just leave as no-op? } trackSingleItem(item: string, properties?: TrackingEventProperties): void { - // eslint-disable-next-line no-console - console.log('PosthogProvider: trackSingleItem' + item, properties); + if (this.verbose) { + // eslint-disable-next-line no-console + console.log('PosthogProvider: trackSingleItem' + item, properties); + } posthog.capture(item, { properties }); } } diff --git a/packages/module/src/tracking/segment_tracking_provider.ts b/packages/module/src/tracking/segment_tracking_provider.ts index bbeb696c0..49f81efc1 100644 --- a/packages/module/src/tracking/segment_tracking_provider.ts +++ b/packages/module/src/tracking/segment_tracking_provider.ts @@ -5,13 +5,16 @@ import { InitProps, TrackingSpi } from './tracking_spi'; export class SegmentTrackingProvider implements TrackingSpi, TrackingApi { private analytics: AnalyticsBrowser | undefined; + private verbose = false; getKey(): string { return 'segmentKey'; } initialize(props: InitProps): void { - // eslint-disable-next-line no-console - console.log('SegmentProvider initialize'); + if (this.verbose) { + // eslint-disable-next-line no-console + console.log('SegmentProvider initialize'); + } const segmentKey = props.segmentKey as string; // We need to create an object here, as ts lint is unhappy otherwise @@ -33,16 +36,20 @@ export class SegmentTrackingProvider implements TrackingSpi, TrackingApi { } identify(userID: string): void { - // eslint-disable-next-line no-console - console.log('SegmentProvider userID: ' + userID); + if (this.verbose) { + // eslint-disable-next-line no-console + console.log('SegmentProvider userID: ' + userID); + } if (this.analytics) { this.analytics.identify(userID); } } trackPageView(url: string | undefined): void { - // eslint-disable-next-line no-console - console.log('SegmentProvider url', url); + if (this.verbose) { + // eslint-disable-next-line no-console + console.log('SegmentProvider url', url); + } if (this.analytics) { if (url) { this.analytics.page(url); @@ -53,8 +60,10 @@ export class SegmentTrackingProvider implements TrackingSpi, TrackingApi { } trackSingleItem(item: string, properties?: TrackingEventProperties): void { - // eslint-disable-next-line no-console - console.log('SegmentProvider: trackSingleItem' + item, properties); + if (this.verbose) { + // eslint-disable-next-line no-console + console.log('SegmentProvider: trackSingleItem' + item, properties); + } if (this.analytics) { this.analytics.track(item, { properties }); } diff --git a/packages/module/src/tracking/tracking_spi.ts b/packages/module/src/tracking/tracking_spi.ts index 7045dc141..71b29148d 100644 --- a/packages/module/src/tracking/tracking_spi.ts +++ b/packages/module/src/tracking/tracking_spi.ts @@ -1,9 +1,13 @@ import { TrackingApi, TrackingEventProperties } from './tracking_api'; -export interface InitProps { - [key: string]: string | number | boolean; +interface BaseProps { + verbose: boolean; } +export type InitProps = { + [key: string]: string | number | boolean; +} & BaseProps; + export interface TrackingSpi extends TrackingApi { // Return a key in InitProps to check if the provided should be enabled getKey: () => string; diff --git a/packages/module/src/tracking/umami_tracking_provider.ts b/packages/module/src/tracking/umami_tracking_provider.ts index 09a818216..f86ffad73 100644 --- a/packages/module/src/tracking/umami_tracking_provider.ts +++ b/packages/module/src/tracking/umami_tracking_provider.ts @@ -9,13 +9,17 @@ declare global { } export class UmamiTrackingProvider implements TrackingSpi, TrackingApi { + private verbose = false; getKey(): string { return 'umamiKey'; } initialize(props: InitProps): void { - // eslint-disable-next-line no-console - console.log('UmamiProvider initialize'); + this.verbose = props.verbose; + if (this.verbose) { + // eslint-disable-next-line no-console + console.log('UmamiProvider initialize'); + } const umamiKey = props.umamiKey as string; const hostUrl = props.umamiHostUrl as string; @@ -35,20 +39,26 @@ export class UmamiTrackingProvider implements TrackingSpi, TrackingApi { } identify(userID: string): void { - // eslint-disable-next-line no-console - console.log('UmamiProvider userID: ' + userID); + if (this.verbose) { + // eslint-disable-next-line no-console + console.log('UmamiProvider userID: ' + userID); + } window.umami?.identify({ userID }); } trackPageView(url: string | undefined): void { - // eslint-disable-next-line no-console - console.log('UmamiProvider url', url); + if (this.verbose) { + // eslint-disable-next-line no-console + console.log('UmamiProvider url', url); + } window.umami?.track({ url }); } trackSingleItem(item: string, properties?: TrackingEventProperties): void { - // eslint-disable-next-line no-console - console.log('UmamiProvider: trackSingleItem' + item, properties); + if (this.verbose) { + // eslint-disable-next-line no-console + console.log('UmamiProvider: trackSingleItem' + item, properties); + } window.umami?.track(item, properties); } } From 858d1e9f385c337b43b6f3e597013202c6392920 Mon Sep 17 00:00:00 2001 From: "Heiko W. Rupp" Date: Fri, 21 Mar 2025 16:43:53 +0100 Subject: [PATCH 2/7] Change initialisation of providers and do a major overhaul of the Umami Provider. --- .eslintrc.json | 2 +- .../chatbot/examples/Analytics/Analytics.md | 30 +++--- .../chatbot/examples/demos/Chatbot.tsx | 6 +- .../src/tracking/console_tracking_provider.ts | 15 +-- .../src/tracking/posthog_tracking_provider.ts | 12 +-- .../src/tracking/segment_tracking_provider.ts | 12 +-- .../src/tracking/trackingProviderProxy.ts | 4 +- packages/module/src/tracking/tracking_api.ts | 2 +- .../module/src/tracking/tracking_registry.ts | 59 +++++++++--- packages/module/src/tracking/tracking_spi.ts | 19 ++-- .../src/tracking/umami_tracking_provider.ts | 92 ++++++++++++++----- 11 files changed, 166 insertions(+), 87 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 0b937e277..5b151c157 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -62,7 +62,7 @@ "dot-notation": "error", "eqeqeq": ["error", "smart"], "guard-for-in": "error", - "indent": ["error", 2], + "indent": ["error", 2, {"SwitchCase": 1}], "max-classes-per-file": ["error", 1], "no-nested-ternary": "error", "no-bitwise": "error", diff --git a/packages/module/patternfly-docs/content/extensions/chatbot/examples/Analytics/Analytics.md b/packages/module/patternfly-docs/content/extensions/chatbot/examples/Analytics/Analytics.md index 1efb79613..f5b540776 100644 --- a/packages/module/patternfly-docs/content/extensions/chatbot/examples/Analytics/Analytics.md +++ b/packages/module/patternfly-docs/content/extensions/chatbot/examples/Analytics/Analytics.md @@ -36,11 +36,12 @@ Note that user code only interacts with: ### Setup -1. Before you can use the `trackingAPI`, you must first supply the API keys of the respective providers. +1. Before you can use the `trackingAPI`, you must first supply the API keys of the respective providers. Providers are enabled by adding them to the `activeProviders` property: ```nolive const initProps: InitProps = { verbose: false, + activeProviders: ['Segment', 'Umami', 'Posthog', 'Console' ], segmentKey: 'TODO-key', // TODO add your key here // segmentCdn: 'https://my.org/cdn', // Set up segment cdn (optional) // segmentIntegrations: { // Provide Segment integrations (optional) @@ -51,10 +52,10 @@ const initProps: InitProps = { }, posthogKey: 'TODO-key', - umamiKey: 'TODO-key', + umamiKey: 'TODO-umami-key', umamiHostUrl: 'http://localhost:3000', // TODO where is your JS provider? + 'umami-data-domains': 'TODO umami data domain', something: 'test', - console: 'true' // Console provider }; ``` @@ -79,22 +80,20 @@ trackingAPI.trackSingleItem("MyEvent", { response: 'Good response' }) #### Tracking providers -Only providers with a matching key in the `InitProps` will be started and used. +Only providers with a matching entry in the `InitProps.activeProviders` array will be started and used. + +Possible values are: +* Umami +* Posthog +* Segment +* Console -```nolive -const initProps: InitProps = { - segmentKey: 'TODO-key', // TODO add your key here - posthogKey: 'TODO-key', - umamiKey: 'TODO-key', - umamiHostUrl: 'http://localhost:3000', // TODO where is your JS provider? - console: true -``` ##### Modifying providers If you know upfront that you only want to use 1 of the supported providers, you can modify `getTrackingProviders()` and remove all other providers in the providers array. -When using the providers you need to add additional dependencies to your package.json file: +When using the providers you may need to add additional dependencies to your package.json file: ```nolive "dependencies": { @@ -102,12 +101,15 @@ When using the providers you need to add additional dependencies to your package "posthog-js": "^1.194.4" ``` +This depends on your local setup. If you pull in +the chatbot codebase as dependency into your project, you are already set. + ##### Adding providers To add another analytics provider, you need to implement 2 interfaces, `TrackingSpi` and `trackingApi`. 1. It is easiest to start by copying the `ConsoleTrackingProvider` -1. The first thing you should do is to provide a correct value in `getKey()` +1. The first thing you should do is to add it to the `Providers` enum in `tracking_spi.ts` 1. Once you are happy enough with the implementation, add it to the array of providers in `getTrackingProviders()` ### Page flow tracking diff --git a/packages/module/patternfly-docs/content/extensions/chatbot/examples/demos/Chatbot.tsx b/packages/module/patternfly-docs/content/extensions/chatbot/examples/demos/Chatbot.tsx index bd54b49fa..cc302b99c 100644 --- a/packages/module/patternfly-docs/content/extensions/chatbot/examples/demos/Chatbot.tsx +++ b/packages/module/patternfly-docs/content/extensions/chatbot/examples/demos/Chatbot.tsx @@ -102,14 +102,14 @@ const initProps: InitProps = { segmentKey: 'TODO-key', // TODO add your key here posthogKey: 'TODO-key', umamiKey: 'TODO-key', - umamiHostUrl: 'http://localhost:3000', // TODO where is your JS provider? + umamiHostUrl: 'http://localhost:3000', // TODO where is your Umami installation? console: true, something: 'test' }; const tracking = getTrackingProviders(initProps); -tracking.identify('user-123'); // TODO get real user id -tracking.trackPageView(window.document.documentURI); +tracking.identify('user-123', { superUser: true }); // TODO get real user id + properties +tracking.trackPageView(window.location.href); const actionEventName = 'MessageAction'; const initialMessages: MessageProps[] = [ diff --git a/packages/module/src/tracking/console_tracking_provider.ts b/packages/module/src/tracking/console_tracking_provider.ts index 4bbf266a5..4c9555a56 100644 --- a/packages/module/src/tracking/console_tracking_provider.ts +++ b/packages/module/src/tracking/console_tracking_provider.ts @@ -1,4 +1,4 @@ -import { TrackingSpi } from './tracking_spi'; +import { InitProps, TrackingSpi } from './tracking_spi'; import { TrackingApi, TrackingEventProperties } from './tracking_api'; export class ConsoleTrackingProvider implements TrackingSpi, TrackingApi { @@ -9,20 +9,19 @@ export class ConsoleTrackingProvider implements TrackingSpi, TrackingApi { console.log('ConsoleProvider pageView ', url); } } - // eslint-disable-next-line @typescript-eslint/no-empty-function - registerProvider(): void {} - initialize(): void { + initialize(props: InitProps): void { + this.verbose = props.verbose; if (this.verbose) { // eslint-disable-next-line no-console console.log('ConsoleProvider initialize'); } } - identify(userID: string): void { + identify(userID: string, userProperties: TrackingEventProperties = {}): void { if (this.verbose) { // eslint-disable-next-line no-console - console.log('ConsoleProvider identify ', userID); + console.log('ConsoleProvider identify ', userID, userProperties); } } @@ -32,8 +31,4 @@ export class ConsoleTrackingProvider implements TrackingSpi, TrackingApi { console.log('ConsoleProvider: ' + item, properties); } } - - getKey(): string { - return 'console'; - } } diff --git a/packages/module/src/tracking/posthog_tracking_provider.ts b/packages/module/src/tracking/posthog_tracking_provider.ts index 19890a975..d97d3a2a5 100644 --- a/packages/module/src/tracking/posthog_tracking_provider.ts +++ b/packages/module/src/tracking/posthog_tracking_provider.ts @@ -5,11 +5,9 @@ import { InitProps, TrackingSpi } from './tracking_spi'; export class PosthogTrackingProvider implements TrackingSpi, TrackingApi { private verbose = false; - getKey(): string { - return 'posthogKey'; - } initialize(props: InitProps): void { + this.verbose = props.verbose; if (this.verbose) { // eslint-disable-next-line no-console console.log('PosthogProvider initialize'); @@ -24,18 +22,18 @@ export class PosthogTrackingProvider implements TrackingSpi, TrackingApi { }); } - identify(userID: string): void { + identify(userID: string, userProperties: TrackingEventProperties = {}): void { if (this.verbose) { // eslint-disable-next-line no-console console.log('PosthogProvider userID: ' + userID); } - posthog.identify(userID); + posthog.identify(userID, userProperties); } trackPageView(url: string | undefined): void { if (this.verbose) { // eslint-disable-next-line no-console - console.log('PostHogProvider url', url); + console.log('PostHogProvider url ', url); } // TODO posthog seems to record that automatically. // How to not clash with this here? Just leave as no-op? @@ -44,7 +42,7 @@ export class PosthogTrackingProvider implements TrackingSpi, TrackingApi { trackSingleItem(item: string, properties?: TrackingEventProperties): void { if (this.verbose) { // eslint-disable-next-line no-console - console.log('PosthogProvider: trackSingleItem' + item, properties); + console.log('PosthogProvider: trackSingleItem ' + item, properties); } posthog.capture(item, { properties }); } diff --git a/packages/module/src/tracking/segment_tracking_provider.ts b/packages/module/src/tracking/segment_tracking_provider.ts index 49f81efc1..d58de53af 100644 --- a/packages/module/src/tracking/segment_tracking_provider.ts +++ b/packages/module/src/tracking/segment_tracking_provider.ts @@ -6,11 +6,9 @@ import { InitProps, TrackingSpi } from './tracking_spi'; export class SegmentTrackingProvider implements TrackingSpi, TrackingApi { private analytics: AnalyticsBrowser | undefined; private verbose = false; - getKey(): string { - return 'segmentKey'; - } initialize(props: InitProps): void { + this.verbose = props.verbose; if (this.verbose) { // eslint-disable-next-line no-console console.log('SegmentProvider initialize'); @@ -35,20 +33,20 @@ export class SegmentTrackingProvider implements TrackingSpi, TrackingApi { ); } - identify(userID: string): void { + identify(userID: string, userProperties: TrackingEventProperties = {}): void { if (this.verbose) { // eslint-disable-next-line no-console console.log('SegmentProvider userID: ' + userID); } if (this.analytics) { - this.analytics.identify(userID); + this.analytics.identify(userID, userProperties); } } trackPageView(url: string | undefined): void { if (this.verbose) { // eslint-disable-next-line no-console - console.log('SegmentProvider url', url); + console.log('SegmentProvider url ', url); } if (this.analytics) { if (url) { @@ -62,7 +60,7 @@ export class SegmentTrackingProvider implements TrackingSpi, TrackingApi { trackSingleItem(item: string, properties?: TrackingEventProperties): void { if (this.verbose) { // eslint-disable-next-line no-console - console.log('SegmentProvider: trackSingleItem' + item, properties); + console.log('SegmentProvider: trackSingleItem ' + item, properties); } if (this.analytics) { this.analytics.track(item, { properties }); diff --git a/packages/module/src/tracking/trackingProviderProxy.ts b/packages/module/src/tracking/trackingProviderProxy.ts index ef25cd935..974a32abb 100644 --- a/packages/module/src/tracking/trackingProviderProxy.ts +++ b/packages/module/src/tracking/trackingProviderProxy.ts @@ -6,9 +6,9 @@ class TrackingProviderProxy implements TrackingApi { this.providers = providers; } - identify(userID: string): void { + identify(userID: string, userProperties: TrackingEventProperties = {}): void { for (const provider of this.providers) { - provider.identify(userID); + provider.identify(userID, userProperties); } } diff --git a/packages/module/src/tracking/tracking_api.ts b/packages/module/src/tracking/tracking_api.ts index fef0fdc7b..5c41aa250 100644 --- a/packages/module/src/tracking/tracking_api.ts +++ b/packages/module/src/tracking/tracking_api.ts @@ -3,7 +3,7 @@ export interface TrackingEventProperties { } export interface TrackingApi { - identify: (userID: string) => void; + identify: (userID: string, userProperties: TrackingEventProperties) => void; trackPageView: (url: string | undefined) => void; diff --git a/packages/module/src/tracking/tracking_registry.ts b/packages/module/src/tracking/tracking_registry.ts index 6f35bedc5..60929667b 100644 --- a/packages/module/src/tracking/tracking_registry.ts +++ b/packages/module/src/tracking/tracking_registry.ts @@ -1,4 +1,4 @@ -import { InitProps, TrackingSpi } from './tracking_spi'; +import { InitProps, Providers, TrackingSpi } from './tracking_spi'; import { TrackingApi } from './tracking_api'; import TrackingProviderProxy from './trackingProviderProxy'; import { ConsoleTrackingProvider } from './console_tracking_provider'; @@ -8,26 +8,59 @@ import { UmamiTrackingProvider } from './umami_tracking_provider'; export const getTrackingProviders = (initProps: InitProps): TrackingApi => { const providers: TrackingSpi[] = []; - providers.push(new SegmentTrackingProvider()); - providers.push(new PosthogTrackingProvider()); - providers.push(new UmamiTrackingProvider()); - // TODO dynamically find and register providers + if (initProps.activeProviders) { + let tmpProps: string[] = initProps.activeProviders; + + // Theoretically we get an array of provider names, but it could also be a CSV string... + if (!Array.isArray(initProps.activeProviders)) { + const tmpString = initProps.activeProviders as string; + if (tmpString && tmpString.indexOf(',') !== -1) { + tmpProps = tmpString.split(','); + } else { + tmpProps = [tmpString]; + } + } + + tmpProps.forEach((provider) => { + switch (Providers[provider]) { + case Providers.Segment: + providers.push(new SegmentTrackingProvider()); + break; + case Providers.Umami: + providers.push(new UmamiTrackingProvider()); + break; + case Providers.Posthog: + providers.push(new PosthogTrackingProvider()); + break; + case Providers.Console: + providers.push(new ConsoleTrackingProvider()); + break; + case Providers.None: // Do nothing, just a placeholder + break; + default: + if (providers.length > 1) { + if (initProps.verbose) { + // eslint-disable-next-line no-console + console.error("Unknown provider '" + provider); + } + } + break; + } + }); + } // Initialize them - const enabledProviders: TrackingSpi[] = []; for (const provider of providers) { - const key = provider.getKey(); - if (Object.keys(initProps).indexOf(key) > -1) { + try { provider.initialize(initProps); - enabledProviders.push(provider); + } catch (e) { + // eslint-disable-next-line no-console + console.error(e); } } - // Add the console provider - const consoleTrackingProvider = new ConsoleTrackingProvider(); - enabledProviders.push(consoleTrackingProvider); // TODO noop- provider? - return new TrackingProviderProxy(enabledProviders); + return new TrackingProviderProxy(providers); }; export default getTrackingProviders; diff --git a/packages/module/src/tracking/tracking_spi.ts b/packages/module/src/tracking/tracking_spi.ts index 71b29148d..ddbbae42e 100644 --- a/packages/module/src/tracking/tracking_spi.ts +++ b/packages/module/src/tracking/tracking_spi.ts @@ -1,7 +1,18 @@ -import { TrackingApi, TrackingEventProperties } from './tracking_api'; +import { TrackingApi } from './tracking_api'; -interface BaseProps { +export enum Providers { + None, + Segment, + Umami, + Posthog, + Console +} + +export type ProviderAsString = keyof typeof Providers; + +export interface BaseProps { verbose: boolean; + activeProviders: [ProviderAsString]; } export type InitProps = { @@ -9,10 +20,6 @@ export type InitProps = { } & BaseProps; export interface TrackingSpi extends TrackingApi { - // Return a key in InitProps to check if the provided should be enabled - getKey: () => string; // Initialize the provider initialize: (props: InitProps) => void; - // Track a single item - trackSingleItem: (item: string, properties?: TrackingEventProperties) => void; } diff --git a/packages/module/src/tracking/umami_tracking_provider.ts b/packages/module/src/tracking/umami_tracking_provider.ts index f86ffad73..f86d79063 100644 --- a/packages/module/src/tracking/umami_tracking_provider.ts +++ b/packages/module/src/tracking/umami_tracking_provider.ts @@ -8,19 +8,25 @@ declare global { } } +// Items in a queue. +// We need to queue up requests until the script is fully loaded +interface queueT { + what: 'i' | 't' | 'p'; // identify, track, pageview + name: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + payload?: any; +} + export class UmamiTrackingProvider implements TrackingSpi, TrackingApi { private verbose = false; - getKey(): string { - return 'umamiKey'; - } + private websiteId: string | undefined; + private queue: queueT[] = []; initialize(props: InitProps): void { this.verbose = props.verbose; - if (this.verbose) { - // eslint-disable-next-line no-console - console.log('UmamiProvider initialize'); - } - const umamiKey = props.umamiKey as string; + this.log('UmamiProvider initialize'); + + this.websiteId = props.umamiKey as string; const hostUrl = props.umamiHostUrl as string; const script = document.createElement('script'); @@ -29,36 +35,76 @@ export class UmamiTrackingProvider implements TrackingSpi, TrackingApi { script.defer = true; // Configure Umami properties - script.setAttribute('data-website-id', umamiKey); - script.setAttribute('data-domains', 'localhost'); // TODO ? + script.setAttribute('data-website-id', this.websiteId); + script.setAttribute('data-host-url', hostUrl); script.setAttribute('data-auto-track', 'false'); - script.setAttribute('data-host-url', hostUrl); // TODO ? - script.setAttribute('data-exclude-search', 'false'); // TODO ? + script.setAttribute('data-exclude-search', 'false'); + + // Now get from config, which may override some of the above. + const UMAMI_PREFIX = 'umami-'; + for (const prop in props) { + if (prop.startsWith(UMAMI_PREFIX)) { + const att = 'data-' + prop.substring(UMAMI_PREFIX.length); + const val = props[prop]; + script.setAttribute(att, String(val)); + } + } + script.onload = () => { + this.log('UmamiProvider script loaded'); + this.flushQueue(); + }; document.body.appendChild(script); } - identify(userID: string): void { - if (this.verbose) { - // eslint-disable-next-line no-console - console.log('UmamiProvider userID: ' + userID); + identify(userID: string, userProperties: TrackingEventProperties = {}): void { + this.log('UmamiProvider userID: ' + userID + ' => ' + JSON.stringify(userProperties)); + if (window.umami) { + window.umami.identify({ userID, userProperties }); + } else { + this.queue.push({ what: 'i', name: userID, payload: userProperties }); } - window.umami?.identify({ userID }); } trackPageView(url: string | undefined): void { - if (this.verbose) { - // eslint-disable-next-line no-console - console.log('UmamiProvider url', url); + this.log('UmamiProvider url ' + url); + if (window.umami) { + window.umami.track({ url, website: this.websiteId }); + } else { + this.queue.push({ what: 'p', name: String(url) }); } - window.umami?.track({ url }); } trackSingleItem(item: string, properties?: TrackingEventProperties): void { + this.log('UmamiProvider: trackSingleItem ' + item + JSON.stringify(properties)); + if (window.umami) { + window.umami.track(item, properties); + } else { + this.queue.push({ what: 't', name: item, payload: properties }); + } + } + + flushQueue(): void { + for (const item of this.queue) { + this.log('Queue flush ' + JSON.stringify(item)); + switch (item.what) { + case 'i': + this.identify(item.name, item.payload); + break; + case 't': + this.trackSingleItem(item.name, item.payload); + break; + case 'p': + this.trackPageView(item.name); + break; + } + } + } + + log(msg: string): void { if (this.verbose) { // eslint-disable-next-line no-console - console.log('UmamiProvider: trackSingleItem' + item, properties); + console.debug('UmamiProvider: ', msg); } - window.umami?.track(item, properties); } } From 198857753fe80df5ebe682e2b8d0a0e792b1d3aa Mon Sep 17 00:00:00 2001 From: "Heiko W. Rupp" Date: Tue, 8 Apr 2025 15:41:17 +0200 Subject: [PATCH 3/7] Update packages/module/patternfly-docs/content/extensions/chatbot/examples/Analytics/Analytics.md Co-authored-by: Erin Donehoo <105813956+edonehoo@users.noreply.github.com> --- .../content/extensions/chatbot/examples/Analytics/Analytics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/module/patternfly-docs/content/extensions/chatbot/examples/Analytics/Analytics.md b/packages/module/patternfly-docs/content/extensions/chatbot/examples/Analytics/Analytics.md index f5b540776..64a67ef56 100644 --- a/packages/module/patternfly-docs/content/extensions/chatbot/examples/Analytics/Analytics.md +++ b/packages/module/patternfly-docs/content/extensions/chatbot/examples/Analytics/Analytics.md @@ -36,7 +36,7 @@ Note that user code only interacts with: ### Setup -1. Before you can use the `trackingAPI`, you must first supply the API keys of the respective providers. Providers are enabled by adding them to the `activeProviders` property: +1. Before you can use the `trackingAPI`, you must first supply the API keys of the respective providers. To enable a provider, it must be added to the `activeProviders` property: ```nolive const initProps: InitProps = { From 4d5a93951980b37ac34f1ba54060834e737ac563 Mon Sep 17 00:00:00 2001 From: "Heiko W. Rupp" Date: Tue, 8 Apr 2025 15:41:46 +0200 Subject: [PATCH 4/7] Update packages/module/patternfly-docs/content/extensions/chatbot/examples/Analytics/Analytics.md Co-authored-by: Erin Donehoo <105813956+edonehoo@users.noreply.github.com> --- .../content/extensions/chatbot/examples/Analytics/Analytics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/module/patternfly-docs/content/extensions/chatbot/examples/Analytics/Analytics.md b/packages/module/patternfly-docs/content/extensions/chatbot/examples/Analytics/Analytics.md index 64a67ef56..e1618ec0e 100644 --- a/packages/module/patternfly-docs/content/extensions/chatbot/examples/Analytics/Analytics.md +++ b/packages/module/patternfly-docs/content/extensions/chatbot/examples/Analytics/Analytics.md @@ -59,7 +59,7 @@ const initProps: InitProps = { }; ``` -Note, that there is also a key `verbose` that allows you to enable debugging output on web-browser console. By default, this is set to `false`. +- **Note:** To enable output debugging via the web-browser console, set the `verbose` key to `true`. By default, this is set to `false`. 1. Once this is done, you can create an instance of the `trackingAPI` and start sending events. From aae839b74a358de0f7c6a20b1fd285280351432c Mon Sep 17 00:00:00 2001 From: "Heiko W. Rupp" Date: Tue, 8 Apr 2025 15:42:13 +0200 Subject: [PATCH 5/7] Update packages/module/patternfly-docs/content/extensions/chatbot/examples/Analytics/Analytics.md Co-authored-by: Erin Donehoo <105813956+edonehoo@users.noreply.github.com> --- .../content/extensions/chatbot/examples/Analytics/Analytics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/module/patternfly-docs/content/extensions/chatbot/examples/Analytics/Analytics.md b/packages/module/patternfly-docs/content/extensions/chatbot/examples/Analytics/Analytics.md index e1618ec0e..070a54f92 100644 --- a/packages/module/patternfly-docs/content/extensions/chatbot/examples/Analytics/Analytics.md +++ b/packages/module/patternfly-docs/content/extensions/chatbot/examples/Analytics/Analytics.md @@ -93,7 +93,7 @@ Possible values are: If you know upfront that you only want to use 1 of the supported providers, you can modify `getTrackingProviders()` and remove all other providers in the providers array. -When using the providers you may need to add additional dependencies to your package.json file: +When using the providers, you might need to add additional dependencies to your package.json file: ```nolive "dependencies": { From 111fdd8e6733dab6ebb42a57a70c54d7f81206b2 Mon Sep 17 00:00:00 2001 From: "Heiko W. Rupp" Date: Tue, 8 Apr 2025 15:48:28 +0200 Subject: [PATCH 6/7] Apply suggestions from code review Co-authored-by: Erin Donehoo <105813956+edonehoo@users.noreply.github.com> --- .../content/extensions/chatbot/examples/Analytics/Analytics.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/module/patternfly-docs/content/extensions/chatbot/examples/Analytics/Analytics.md b/packages/module/patternfly-docs/content/extensions/chatbot/examples/Analytics/Analytics.md index 070a54f92..558b85cf6 100644 --- a/packages/module/patternfly-docs/content/extensions/chatbot/examples/Analytics/Analytics.md +++ b/packages/module/patternfly-docs/content/extensions/chatbot/examples/Analytics/Analytics.md @@ -101,8 +101,7 @@ When using the providers, you might need to add additional dependencies to your "posthog-js": "^1.194.4" ``` -This depends on your local setup. If you pull in -the chatbot codebase as dependency into your project, you are already set. +Depending on your local setup, this might not be necessary. For example, if you pull the ChatBot codebase as a dependency into your project, you don't need to add it as an additional dependency in your package.json. ##### Adding providers From 44b9026fc1c6c10fe20a3c50dc2205fe6984102a Mon Sep 17 00:00:00 2001 From: "Heiko W. Rupp" Date: Thu, 10 Apr 2025 08:58:46 +0200 Subject: [PATCH 7/7] Update packages/module/patternfly-docs/content/extensions/chatbot/examples/Analytics/Analytics.md Co-authored-by: Erin Donehoo <105813956+edonehoo@users.noreply.github.com> --- .../content/extensions/chatbot/examples/Analytics/Analytics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/module/patternfly-docs/content/extensions/chatbot/examples/Analytics/Analytics.md b/packages/module/patternfly-docs/content/extensions/chatbot/examples/Analytics/Analytics.md index 9875fe231..370329ef3 100644 --- a/packages/module/patternfly-docs/content/extensions/chatbot/examples/Analytics/Analytics.md +++ b/packages/module/patternfly-docs/content/extensions/chatbot/examples/Analytics/Analytics.md @@ -108,7 +108,7 @@ Depending on your local setup, this might not be necessary. For example, if you To add another analytics provider, you need to implement 2 interfaces, `TrackingSpi` and `trackingApi`. 1. It is easiest to start by copying the `ConsoleTrackingProvider` -1. The first thing you should do is to add it to the `Providers` enum in `tracking_spi.ts` +1. Add an entry for your new provider to the `Providers` enum in `tracking_spi.ts` 1. Once you are happy enough with the implementation, add it to the array of providers in `getTrackingProviders()` ### Page flow tracking