Skip to content
Merged
2 changes: 1 addition & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +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. To enable a provider, it must be added 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)
Expand All @@ -50,13 +52,15 @@ 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
};
```

- **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.

```nolive
Expand All @@ -76,35 +80,35 @@ 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 might need to add additional dependencies to your package.json file:

```nolive
"dependencies": {
"@segment/analytics-next": "^1.76.0",
"posthog-js": "^1.194.4"
```

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

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. 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,17 +98,18 @@ 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',
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[] = [
Expand Down
38 changes: 21 additions & 17 deletions packages/module/src/tracking/console_tracking_provider.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,34 @@
import { TrackingSpi } from './tracking_spi';
import { InitProps, 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');
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 {
// eslint-disable-next-line no-console
console.log('ConsoleProvider identify', userID);
identify(userID: string, userProperties: TrackingEventProperties = {}): void {
if (this.verbose) {
// eslint-disable-next-line no-console
console.log('ConsoleProvider identify ', userID, userProperties);
}
}

trackSingleItem(item: string, properties?: TrackingEventProperties): void {
// eslint-disable-next-line no-console
console.log('ConsoleProvider: ' + item, properties);
}

getKey(): string {
return 'console';
if (this.verbose) {
// eslint-disable-next-line no-console
console.log('ConsoleProvider: ' + item, properties);
}
}
}
33 changes: 20 additions & 13 deletions packages/module/src/tracking/posthog_tracking_provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ import { TrackingApi, TrackingEventProperties } from './tracking_api';
import { InitProps, TrackingSpi } from './tracking_spi';

export class PosthogTrackingProvider implements TrackingSpi, TrackingApi {
getKey(): string {
return 'posthogKey';
}
private verbose = false;

initialize(props: InitProps): void {
// eslint-disable-next-line no-console
console.log('PosthogProvider initialize');
this.verbose = props.verbose;
if (this.verbose) {
// eslint-disable-next-line no-console
console.log('PosthogProvider initialize');
}
const posthogKey = props.posthogKey as string;

posthog.init(posthogKey, {
Expand All @@ -21,22 +22,28 @@ export class PosthogTrackingProvider implements TrackingSpi, TrackingApi {
});
}

identify(userID: string): void {
// eslint-disable-next-line no-console
console.log('PosthogProvider userID: ' + userID);
posthog.identify(userID);
identify(userID: string, userProperties: TrackingEventProperties = {}): void {
if (this.verbose) {
// eslint-disable-next-line no-console
console.log('PosthogProvider userID: ' + userID);
}
posthog.identify(userID, userProperties);
}

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 });
}
}
33 changes: 20 additions & 13 deletions packages/module/src/tracking/segment_tracking_provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ import { InitProps, TrackingSpi } from './tracking_spi';

export class SegmentTrackingProvider implements TrackingSpi, TrackingApi {
private analytics: AnalyticsBrowser | undefined;
getKey(): string {
return 'segmentKey';
}
private verbose = false;

initialize(props: InitProps): void {
// eslint-disable-next-line no-console
console.log('SegmentProvider initialize');
this.verbose = props.verbose;
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
Expand All @@ -32,17 +33,21 @@ export class SegmentTrackingProvider implements TrackingSpi, TrackingApi {
);
}

identify(userID: string): void {
// eslint-disable-next-line no-console
console.log('SegmentProvider userID: ' + userID);
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 {
// 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);
Expand All @@ -53,8 +58,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 });
}
Expand Down
4 changes: 2 additions & 2 deletions packages/module/src/tracking/trackingProviderProxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down
2 changes: 1 addition & 1 deletion packages/module/src/tracking/tracking_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
59 changes: 46 additions & 13 deletions packages/module/src/tracking/tracking_registry.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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;
Loading
Loading