Skip to content

Commit

Permalink
Added support for modules to submit module analytic events. (#706)
Browse files Browse the repository at this point in the history
  • Loading branch information
johnman authored Apr 17, 2024
1 parent f3a9087 commit 15db1b6
Show file tree
Hide file tree
Showing 9 changed files with 126 additions and 4 deletions.
1 change: 1 addition & 0 deletions how-to/workspace-platform-starter/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## v18.0.0

- Module Helpers now provide a getAnalyticsClient (it is marked as optional and the result could be undefined so it leaves the option for it to be denied to a module or removed). This client supports a ModuleAnalytic event which will have a source of Module assigned to it (you can still specify type and use the data property to provide additional module specific information). This data will be passed to the analyticProviders that receive the Workspace Analytic events. See [How to Configure Analytics](./docs/how-to-configure-analytics.md).
- Added a cloud interop override module so that you can easily test out OpenFin's cloud interop offering. See [How To Add Cloud Interop To Your Interop Broker](./docs/how-to-add-cloud-interop-to-your-interop-broker.md).
- Broke up the build process to make it easier to just build your modules. `npm run build` still builds everything and `npm run build-client` still builds all client related code but now if you change framework files you can use `npm run build-framework`, or if you modify our starter modules you can use `npm run build-starter-modules` or if you just want to build your modules (that are listed in webpack.config.js) then you can use `npm run build-client-modules`. This will let you have a much faster build.
- npm run launch now launches via the fins link when running on mac instead of the node adapter.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,15 @@ export async function init(
if (options) {
logger.info("Initializing with options", options);
modules = await loadModules<AnalyticsModule>(options, "analytics");
await initializeModules(modules, helpers);
// eslint-disable-next-line @typescript-eslint/unbound-method
const { getAnalyticsClient, ...analyticsHelper } = helpers;

if (getAnalyticsClient) {
logger.info(
"getAnalyticsClient is defined as part of helpers but not passed to analytics modules. This is to prevent an endless loop where an analytics module may be feeding events to itself."
);
}
await initializeModules(modules, analyticsHelper);
isAnalyticsEnabled = modules.length > 0;
}
}
Expand Down
25 changes: 25 additions & 0 deletions how-to/workspace-platform-starter/client/src/framework/modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
type BrowserWindowModule,
type WorkspacePlatformModule
} from "@openfin/workspace-platform";
import { handleAnalytics } from "./analytics";
import { getApp, getApps } from "./apps";
import { checkCondition, conditionChanged } from "./conditions";
import * as connectionProvider from "./connections";
Expand All @@ -17,6 +18,12 @@ import { createLogger } from "./logger-provider";
import { MANIFEST_TYPES } from "./manifest-types";
import * as Menu from "./menu";
import { launchPage, launchView } from "./platform/browser";
import {
MODULE_ANALYTICS_SOURCE,
type AnalyticsClient,
type ModuleAnalyticsEvent,
type PlatformAnalyticsEvent
} from "./shapes/analytics-shapes";
import type { PlatformApp, PlatformAppIdentifier, UpdatableLaunchPreference } from "./shapes/app-shapes";
import type { ConditionContextTypes, ConditionsClient } from "./shapes/conditions-shapes";
import type { ConnectionValidationOptions, ConnectionValidationResponse } from "./shapes/connection-shapes";
Expand Down Expand Up @@ -283,6 +290,7 @@ export function getDefaultHelpers(): ModuleHelpers {
sessionId: passedSessionId,
bringAppToFront: bringToFront,
getPlatform: getCurrentSync,
getAnalyticsClient,
getApps: async (): Promise<PlatformApp[]> => {
logger.info("getApps: getting public apps for module.");
return getApps({ private: false });
Expand Down Expand Up @@ -445,6 +453,23 @@ async function getMenuClient(): Promise<MenuClient> {
};
}

/**
* Get analytics client.
* @returns The analytics client.
*/
async function getAnalyticsClient(): Promise<AnalyticsClient | undefined> {
return {
handleAnalytics: async (events: ModuleAnalyticsEvent[]): Promise<void> => {
if (Array.isArray(events)) {
const platformAnalyticEvents: PlatformAnalyticsEvent[] = events.map<PlatformAnalyticsEvent>(
(entry) => ({ ...entry, source: MODULE_ANALYTICS_SOURCE })
);
return handleAnalytics(platformAnalyticEvents);
}
}
};
}

/**
* Get the conditions client to use with the modules.
* @returns The conditions client.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ export interface AnalyticsProviderOptions extends ModuleList {
*/
export const PLATFORM_ANALYTICS_SOURCE = "WorkspacePlatform";

/**
* Additional source for module events.
*/
export const MODULE_ANALYTICS_SOURCE = "Module";

/**
* The data for the analytics events that need to be handled. Extends the platform AnalyticsEvent with additional data
* source.
Expand All @@ -38,10 +43,28 @@ export interface PlatformAnalyticsEvent extends Omit<AnalyticsEvent, "source"> {
/**
* The source of the event.
*/
source: typeof PLATFORM_ANALYTICS_SOURCE | AnalyticsSource;
source: typeof PLATFORM_ANALYTICS_SOURCE | typeof MODULE_ANALYTICS_SOURCE | AnalyticsSource;

/**
* The timestamp for the event.
*/
timestamp: Date;
}

/**
* The data for the analytics events that need to be handled. Extends the platform AnalyticsEvent but enforces source as
* Module. Modules can use type (to specify module id) and use action, value and data to provide module specific information if required.
*/
export type ModuleAnalyticsEvent = Omit<PlatformAnalyticsEvent, "source" | "entityId">;

/**
* Provides a client for handling analytics events.
*/
export interface AnalyticsClient {
/**
* Handle a list of analytics events.
* @param events The events to handle.
* @returns Nothing.
*/
handleAnalytics(events: ModuleAnalyticsEvent[]): Promise<void>;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type OpenFin from "@openfin/core";
import type { BrowserWindowModule, WorkspacePlatformModule } from "@openfin/workspace-platform";
import type { AnalyticsClient } from "./analytics-shapes";
import type { PlatformApp, PlatformAppIdentifier, UpdatableLaunchPreference } from "./app-shapes";
import type { ConditionsClient } from "./conditions-shapes";
import type { ConnectionValidationOptions, ConnectionValidationResponse } from "./connection-shapes";
Expand Down Expand Up @@ -93,6 +94,12 @@ export interface ModuleHelpers {
*/
getPlatform?(): WorkspacePlatformModule;

/**
* Get Analytics Client.
* @returns The analytics client that can be used to feed analytics to the analytics provider or undefined if it isn't available.
*/
getAnalyticsClient?(): Promise<AnalyticsClient | undefined>;

/**
* Get the list of apps supported by this platform and/or user.
* @returns The list of platform apps available from the module.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ export interface AnalyticsProviderOptions extends ModuleList {
* Additional source for workspace platform events.
*/
export declare const PLATFORM_ANALYTICS_SOURCE = "WorkspacePlatform";
/**
* Additional source for module events.
*/
export declare const MODULE_ANALYTICS_SOURCE = "Module";
/**
* The data for the analytics events that need to be handled. Extends the platform AnalyticsEvent with additional data
* source.
Expand All @@ -34,9 +38,25 @@ export interface PlatformAnalyticsEvent extends Omit<AnalyticsEvent, "source"> {
/**
* The source of the event.
*/
source: typeof PLATFORM_ANALYTICS_SOURCE | AnalyticsSource;
source: typeof PLATFORM_ANALYTICS_SOURCE | typeof MODULE_ANALYTICS_SOURCE | AnalyticsSource;
/**
* The timestamp for the event.
*/
timestamp: Date;
}
/**
* The data for the analytics events that need to be handled. Extends the platform AnalyticsEvent but enforces source as
* Module. Modules can use type (to specify module id) and use action, value and data to provide module specific information if required.
*/
export type ModuleAnalyticsEvent = Omit<PlatformAnalyticsEvent, "source" | "entityId">;
/**
* Provides a client for handling analytics events.
*/
export interface AnalyticsClient {
/**
* Handle a list of analytics events.
* @param events The events to handle.
* @returns Nothing.
*/
handleAnalytics(events: ModuleAnalyticsEvent[]): Promise<void>;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type OpenFin from "@openfin/core";
import type { BrowserWindowModule, WorkspacePlatformModule } from "@openfin/workspace-platform";
import type { AnalyticsClient } from "./analytics-shapes";
import type { PlatformApp, PlatformAppIdentifier, UpdatableLaunchPreference } from "./app-shapes";
import type { ConditionsClient } from "./conditions-shapes";
import type { ConnectionValidationOptions, ConnectionValidationResponse } from "./connection-shapes";
Expand Down Expand Up @@ -80,6 +81,11 @@ export interface ModuleHelpers {
* @returns The current platform.
*/
getPlatform?(): WorkspacePlatformModule;
/**
* Get Analytics Client.
* @returns The analytics client that can be used to feed analytics to the analytics provider or undefined if it isn't available.
*/
getAnalyticsClient?(): Promise<AnalyticsClient | undefined>;
/**
* Get the list of apps supported by this platform and/or user.
* @returns The list of platform apps available from the module.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,15 @@ The following features are currently extendable using modules:
- analytics
- auth
- conditions
- content-creation
- endpoint
- initOptions
- init-options
- integrations
- interop-override
- lifecycle
- log
- menus
- share

## Configuration

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,34 @@ public async handleAnalytics(events: AnalyticsEvent[]) {
}
```

## Module Helper - getAnalyticsClient

The module helper that gets passed to modules has an optional getAnalyticsClient function that might be available to your module. If available you can use it to request an analytics client. This may come back undefined if there is a reason why it cannot be provided to that module. If available it will let you pass one or more ModuleAnalyticEvents. These events do not have a source setting as this is set to Module but you can specify type and there is a data setting where you can put event/module specific information. This will be passed to the analytics modules so that they can take that feed and do something with it (send to a backend, display on as a report etc).

> **INFO:** This helper method is not passed to an analytics module to prevent accidental endless loops where submitting an analytical event would go back to the module that submitted it.
```javascript
if(helpers?.getAnalyticsClient !== undefined) {
const analyticsClient = await helpers.getAnalyticsClient();
...
if(analyticsClient !== undefined) {
analyticsClient.handleAnalytics([
{
action: "Event Happened",
timestamp: new Date(),
type: this._definition?.id ?? "my module",
value: "Custom Value",
data: { }
}
]);
}
}
```
### Existing analytics
If you are creating an action for the Browser or a Home result through an integration then please remember that since they are being actioned by Workspace Components (Browser/Home) they will already be generating analytical events so you might not need to make use of this helper client.
## Generate From Template
You can generate the scaffold for a new module by using the following command line, where "My Analytics" is the name you want to give your module:
Expand Down

0 comments on commit 15db1b6

Please sign in to comment.