From 7aa6ddd78cef5559c2bfb88fb86c6b3adfbd46d5 Mon Sep 17 00:00:00 2001 From: John Date: Sun, 17 Mar 2024 23:24:54 +0000 Subject: [PATCH] Broke out default interop broker into a module Evaluating the previous approach where modules could be specified in an array and the default could be applied before or after highlighted there may be cases where you need to have an interop override load before and after the default implementation so the old approach was too restrictive and added complexity for understanding. The default implementation has been broken out into an interop override module and the helper methods have been updated to make this possible (in a backwards compatible way). The default implementation does now need to be listed in the array but it gives a lot more options and makes it easier to understand that not including it means you are not loading that logic needlessly. --- .../workspace-platform-starter/CHANGELOG.md | 5 +- how-to/workspace-platform-starter/README.md | 1 + .../client/src/framework/modules.ts | 22 +++- .../client/src/framework/platform/interop.ts | 20 +--- .../framework/shapes/interopbroker-shapes.ts | 8 -- .../src/framework/shapes/module-shapes.ts | 31 +++++- .../broker/app-intent-helper.ts | 8 +- .../broker/client-registration-helper.ts | 6 +- .../broker/intent-resolver-helper.ts | 13 ++- .../broker/wps-interop-override.ts} | 105 ++++++++++++------ .../wps-interop-override/index.ts | 9 ++ .../wps-interop-override/interop-override.ts | 74 ++++++++++++ .../wps-interop-override/shapes.ts | 7 ++ .../module/shapes/interopbroker-shapes.d.ts | 8 -- .../types/module/shapes/module-shapes.d.ts | 29 ++++- .../client/webpack.config.js | 28 +++++ .../how-to-customize-your-interop-broker.md | 100 ++++++++--------- .../public/manifest.fin.json | 19 +++- .../public/pack.manifest.fin.json | 14 ++- .../public/schemas/settings.schema.json | 9 -- .../public/schemas/wps.manifest.schema.json | 5 - .../public/settings.json | 41 ++++++- .../public/third.manifest.fin.json | 15 ++- 23 files changed, 406 insertions(+), 171 deletions(-) rename how-to/workspace-platform-starter/client/src/{framework/platform => modules/interop-override/wps-interop-override}/broker/app-intent-helper.ts (94%) rename how-to/workspace-platform-starter/client/src/{framework/platform => modules/interop-override/wps-interop-override}/broker/client-registration-helper.ts (98%) rename how-to/workspace-platform-starter/client/src/{framework/platform => modules/interop-override/wps-interop-override}/broker/intent-resolver-helper.ts (90%) rename how-to/workspace-platform-starter/client/src/{framework/platform/broker/interop-override.ts => modules/interop-override/wps-interop-override/broker/wps-interop-override.ts} (93%) create mode 100644 how-to/workspace-platform-starter/client/src/modules/interop-override/wps-interop-override/index.ts create mode 100644 how-to/workspace-platform-starter/client/src/modules/interop-override/wps-interop-override/interop-override.ts create mode 100644 how-to/workspace-platform-starter/client/src/modules/interop-override/wps-interop-override/shapes.ts diff --git a/how-to/workspace-platform-starter/CHANGELOG.md b/how-to/workspace-platform-starter/CHANGELOG.md index 36823c1965..72d90d1ae8 100644 --- a/how-to/workspace-platform-starter/CHANGELOG.md +++ b/how-to/workspace-platform-starter/CHANGELOG.md @@ -4,10 +4,7 @@ - Improved performance of switching schemes - Improved performance of computing dock configuration, especially on theme changes. -- Added modules as an option for platformProvider.interop settings and added interopOverride as a module generation option when calling npm run generate-module e.g. npm run generate-module interopOverride 'my override'. Also added the option of changing when/if the Workspace Platform Starter default interop override runs: - - after (default) - run the Workspace Platform Starter default broker implementation after your interop override modules (so the default implementation is the base class) - - before - run the Workspace Platform Starter default broker implementation before your interop override modules (so your interop overrides are the base class for our default implementation) - - never - never use the Workspace Platform Starter default implementation (So you will be responsible for adding full interop broker support through your modules) +- Breaking Change (if you do not update your manifest): Added modules as an option for platformProvider.interop settings and made the workspace platform starter interop override a module (so you can decide to load it, chain it with other overrides or exclude it). Please see the new document [how to customize your interop broker](./docs/how-to-customize-your-interop-broker.md). If you want the default interop broker to check endpoints to see if a context type should be enriched through an endpoint then you need to add the wps-interop-override module id to the endpoint clients array in the endpointProvider (see the manifest.fin.json as an example). ## v17.2.0 diff --git a/how-to/workspace-platform-starter/README.md b/how-to/workspace-platform-starter/README.md index 4172d20eb9..e958f4f705 100644 --- a/how-to/workspace-platform-starter/README.md +++ b/how-to/workspace-platform-starter/README.md @@ -49,6 +49,7 @@ The information below provides information related to configuring and using the | [Add Intent Support To Your App](./docs/how-to-add-intent-support-to-your-app.md) | If you have added one or more apps to your platform you may want one to trigger a workflow in another using FDC3. | | [Configure Your Platform's Intent Support](./docs/how-to-configure-fdc3-intents.md) | How to configure your platform's Intent support. What UI to present to the user if they have to make an application selection after an intent is raised. | | [Add FDC3 Open Support To Your App](./docs/how-to-add-open-support-to-your-app.md) | If you have added one or more apps to your platform you may wish to have them easily opened using fdc3.open with the option of passing context. | +| [Customize Your Interop Broker](./docs/how-to-customize-your-interop-broker.md) | You now have control over the interop broker beyond settings. You can use the default implementation, replace it or extend it without having to touch the main codebase. | | [Use Notifications](./docs/how-to-use-notifications.md) | Your platform or you app(s) may want to get the end user's attention and capture a response. | | [Customize The Bootstrapping Process](./docs/how-to-customize-the-bootstrapping-process.md) | This section covers how you can manage what gets registered and what background behavior do you want to run (e.g. do you want to register all of the Workspace Components or just some of them?). | | [Theme Your Platform](./docs/how-to-theme-your-platform.md) | The workspace components support a dark theme out of the box. This section covers how to define the theme and logo for your platform and some tools that make that process easier. | diff --git a/how-to/workspace-platform-starter/client/src/framework/modules.ts b/how-to/workspace-platform-starter/client/src/framework/modules.ts index f5a49721f7..1ab96fec69 100644 --- a/how-to/workspace-platform-starter/client/src/framework/modules.ts +++ b/how-to/workspace-platform-starter/client/src/framework/modules.ts @@ -6,18 +6,20 @@ import { } from "@openfin/workspace-platform"; import { getApp, getApps } from "./apps"; import { checkCondition, conditionChanged } from "./conditions"; +import * as connectionProvider from "./connections"; import * as Dialog from "./dialog"; import { getEndpointClient } from "./endpoint"; import type { EndpointClient } from "./endpoint-client"; import * as favoriteProvider from "./favorite"; -import { launch } from "./launch"; +import { bringToFront, launch } from "./launch"; import { subscribeLifecycleEvent, unsubscribeLifecycleEvent } from "./lifecycle"; import { createLogger } from "./logger-provider"; import { MANIFEST_TYPES } from "./manifest-types"; import * as Menu from "./menu"; import { launchPage, launchView } from "./platform/browser"; -import type { PlatformApp, UpdatableLaunchPreference } from "./shapes/app-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"; import type { DialogClient } from "./shapes/dialog-shapes"; import type { FavoriteClient } from "./shapes/favorite-shapes"; import type { Logger } from "./shapes/logger-shapes"; @@ -279,6 +281,7 @@ export async function closedownModule< export function getDefaultHelpers(): ModuleHelpers { return { sessionId: passedSessionId, + bringAppToFront: bringToFront, getPlatform: getCurrentSync, getApps: async (): Promise => { logger.info("getApps: getting public apps for module."); @@ -293,16 +296,27 @@ export function getDefaultHelpers(): ModuleHelpers { getConditionsClient, getShareClient, getDialogClient, - launchApp: async (appId: string, launchPreference?: UpdatableLaunchPreference): Promise => { + isConnectionValid: async ( + identity: OpenFin.Identity, + payload?: unknown, + options?: ConnectionValidationOptions + ): Promise => + connectionProvider.isConnectionValid(identity, payload, options), + launchApp: async ( + appId: string, + launchPreference?: UpdatableLaunchPreference + ): Promise => { logger.info(`launchApp: Looking up appId: ${appId}`); const app = await getApp(appId); + let result: PlatformAppIdentifier[] | undefined; if (isEmpty(app)) { logger.warn(`launchApp: The specified appId: ${appId} is not listed in this platform.`); } else { logger.info(`launchApp: Launching app with appId: ${appId}`); - await launch(app, launchPreference); + result = await launch(app, launchPreference); logger.info(`launchApp: App with appId: ${appId} launched.`); } + return result; }, launchPage: async ( pageId: string, diff --git a/how-to/workspace-platform-starter/client/src/framework/platform/interop.ts b/how-to/workspace-platform-starter/client/src/framework/platform/interop.ts index d1c6dfb1f7..4419db0616 100644 --- a/how-to/workspace-platform-starter/client/src/framework/platform/interop.ts +++ b/how-to/workspace-platform-starter/client/src/framework/platform/interop.ts @@ -5,8 +5,6 @@ import type { WindowPositioningOptions } from "../shapes/browser-shapes"; import type { PlatformInteropOverride, PlatformInteropOverrideOptions } from "../shapes/interopbroker-shapes"; import type { ModuleDefinition, ModuleEntry, ModuleHelpers } from "../shapes/module-shapes"; import type { PlatformProviderOptions } from "../shapes/platform-shapes"; -import { isEmpty } from "../utils"; -import { getConstructorOverride } from "./broker/interop-override"; const logger = createLogger("InteropProvider"); const allOverrides: OpenFin.ConstructorOverride[] = []; @@ -24,7 +22,7 @@ export async function init( windowPositioningOptions: WindowPositioningOptions, helpers: ModuleHelpers ): Promise { - if (options) { + if (options && !isInitialized) { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { modules: moduleOptions, ...settings } = options?.interop ?? {}; const interopOverrideSettings: PlatformInteropOverrideOptions | undefined = settings; @@ -52,28 +50,12 @@ export async function init( logger.info("Getting interop overrides..."); - if ( - interopOverrideSettings?.defaultBrokerStrategy === "after" || - isEmpty(interopOverrideSettings?.defaultBrokerStrategy) - ) { - logger.info( - "Adding default interop override so it executes after the list of custom interop overrides" - ); - allOverrides.push(await getConstructorOverride(interopOverrideSettings)); - } for (const interopModule of modules) { const interopConstructor = await interopModule.implementation.getConstructorOverride(interopOverrideSettings); allOverrides.push(interopConstructor); logger.info(`Added interopOverride module: ${interopModule.definition.id}`); } - if (interopOverrideSettings?.defaultBrokerStrategy === "before") { - logger.info("Adding default interop override so it runs before the list of custom interop overrides"); - allOverrides.push(await getConstructorOverride(interopOverrideSettings)); - } - if (interopOverrideSettings?.defaultBrokerStrategy === "never") { - logger.info("The default interop override will not be added and will not be executed."); - } logger.info("Finished setting up interop overrides."); isInitialized = true; } diff --git a/how-to/workspace-platform-starter/client/src/framework/shapes/interopbroker-shapes.ts b/how-to/workspace-platform-starter/client/src/framework/shapes/interopbroker-shapes.ts index b54bcd9094..b4017ae78c 100644 --- a/how-to/workspace-platform-starter/client/src/framework/shapes/interopbroker-shapes.ts +++ b/how-to/workspace-platform-starter/client/src/framework/shapes/interopbroker-shapes.ts @@ -37,14 +37,6 @@ export type PlatformInteropOverrideOptions = Omit; + /** * Get the current platform. * @returns The current platform. @@ -166,14 +175,30 @@ export interface ModuleHelpers { */ getDialogClient?(): Promise; + /** + * If the platform has been configured to list supported connections then this API can provide a way of validating the connection. + * @param identity The identity of the connection. + * @param payload The payload to validate if provided. + * @param options The options for the validation (provides additional information such as the type of connection that is trying to be made). + * @returns The response from the validation. + */ + isConnectionValid?( + identity: OpenFin.Identity, + payload?: unknown, + options?: ConnectionValidationOptions + ): Promise; + /** * If available, this function lets you request the launch of an application that is available to this platform and * the current user. * @param appId The id of the application that is registered against the currently running platform * @param launchPreference If the app supports launch preferences then these can be passed. - * @returns Nothing. + * @returns An array of the platform identities that related from the launch or nothing if nothing was launched. */ - launchApp?(appId: string, launchPreference?: UpdatableLaunchPreference): Promise; + launchApp?( + appId: string, + launchPreference?: UpdatableLaunchPreference + ): Promise; /** * Launch a page in the workspace. diff --git a/how-to/workspace-platform-starter/client/src/framework/platform/broker/app-intent-helper.ts b/how-to/workspace-platform-starter/client/src/modules/interop-override/wps-interop-override/broker/app-intent-helper.ts similarity index 94% rename from how-to/workspace-platform-starter/client/src/framework/platform/broker/app-intent-helper.ts rename to how-to/workspace-platform-starter/client/src/modules/interop-override/wps-interop-override/broker/app-intent-helper.ts index 2907544c7e..220519cbfa 100644 --- a/how-to/workspace-platform-starter/client/src/framework/platform/broker/app-intent-helper.ts +++ b/how-to/workspace-platform-starter/client/src/modules/interop-override/wps-interop-override/broker/app-intent-helper.ts @@ -1,7 +1,7 @@ -import type { AppsForIntent, PlatformApp } from "../../shapes/app-shapes"; -import type { AppIntents } from "../../shapes/fdc3-2-0-shapes"; -import type { Logger } from "../../shapes/logger-shapes"; -import { isEmpty } from "../../utils"; +import type { AppsForIntent, PlatformApp } from "workspace-platform-starter/shapes/app-shapes"; +import type { AppIntents } from "workspace-platform-starter/shapes/fdc3-2-0-shapes"; +import type { Logger } from "workspace-platform-starter/shapes/logger-shapes"; +import { isEmpty } from "workspace-platform-starter/utils"; /** * The App Intent Helper inspects app catalogs to discover supported intents and contexts. diff --git a/how-to/workspace-platform-starter/client/src/framework/platform/broker/client-registration-helper.ts b/how-to/workspace-platform-starter/client/src/modules/interop-override/wps-interop-override/broker/client-registration-helper.ts similarity index 98% rename from how-to/workspace-platform-starter/client/src/framework/platform/broker/client-registration-helper.ts rename to how-to/workspace-platform-starter/client/src/modules/interop-override/wps-interop-override/broker/client-registration-helper.ts index a0dfe5473d..721f630685 100644 --- a/how-to/workspace-platform-starter/client/src/framework/platform/broker/client-registration-helper.ts +++ b/how-to/workspace-platform-starter/client/src/modules/interop-override/wps-interop-override/broker/client-registration-helper.ts @@ -7,9 +7,9 @@ import type { IntentRegistrationPayload, CaptureApiPayload, ApiMetadata -} from "../../shapes/interopbroker-shapes"; -import type { Logger } from "../../shapes/logger-shapes"; -import { isEmpty, isStringValue } from "../../utils"; +} from "workspace-platform-starter/shapes/interopbroker-shapes"; +import type { Logger } from "workspace-platform-starter/shapes/logger-shapes"; +import { isEmpty, isStringValue } from "workspace-platform-starter/utils"; /** * Used to track client interactions with a broker. diff --git a/how-to/workspace-platform-starter/client/src/framework/platform/broker/intent-resolver-helper.ts b/how-to/workspace-platform-starter/client/src/modules/interop-override/wps-interop-override/broker/intent-resolver-helper.ts similarity index 90% rename from how-to/workspace-platform-starter/client/src/framework/platform/broker/intent-resolver-helper.ts rename to how-to/workspace-platform-starter/client/src/modules/interop-override/wps-interop-override/broker/intent-resolver-helper.ts index ce6c89e363..31ed59c835 100644 --- a/how-to/workspace-platform-starter/client/src/framework/platform/broker/intent-resolver-helper.ts +++ b/how-to/workspace-platform-starter/client/src/modules/interop-override/wps-interop-override/broker/intent-resolver-helper.ts @@ -1,11 +1,14 @@ import { ResolveError } from "@finos/fdc3"; import type OpenFin from "@openfin/core"; import type { AppIntent } from "@openfin/workspace-platform"; -import type { PlatformApp } from "../../shapes/app-shapes"; -import type { IntentResolverResponse, IntentResolverOptions } from "../../shapes/interopbroker-shapes"; -import type { Logger } from "../../shapes/logger-shapes"; -import { formatError, isEmpty } from "../../utils"; -import { centerContentInIdentity } from "../../utils-position"; +import type { PlatformApp } from "workspace-platform-starter/shapes/app-shapes"; +import type { + IntentResolverResponse, + IntentResolverOptions +} from "workspace-platform-starter/shapes/interopbroker-shapes"; +import type { Logger } from "workspace-platform-starter/shapes/logger-shapes"; +import { formatError, isEmpty } from "workspace-platform-starter/utils"; +import { centerContentInIdentity } from "workspace-platform-starter/utils-position"; /** * An Intent Resolver Used for resolving intent selection. diff --git a/how-to/workspace-platform-starter/client/src/framework/platform/broker/interop-override.ts b/how-to/workspace-platform-starter/client/src/modules/interop-override/wps-interop-override/broker/wps-interop-override.ts similarity index 93% rename from how-to/workspace-platform-starter/client/src/framework/platform/broker/interop-override.ts rename to how-to/workspace-platform-starter/client/src/modules/interop-override/wps-interop-override/broker/wps-interop-override.ts index 2ef262621f..b78e8ff0e5 100644 --- a/how-to/workspace-platform-starter/client/src/framework/platform/broker/interop-override.ts +++ b/how-to/workspace-platform-starter/client/src/modules/interop-override/wps-interop-override/broker/wps-interop-override.ts @@ -8,21 +8,17 @@ import { type ContextMetadata } from "@finos/fdc3"; import type OpenFin from "@openfin/core"; -import { getApp, getApps } from "../../apps"; -import * as connectionProvider from "../../connections"; -import { hasEndpoint, requestResponse } from "../../endpoint"; -import { mapToAppMetaData as mapTo12AppMetaData } from "../../fdc3/1.2/mapper"; -import { mapToAppMetaData as mapTo20AppMetaData } from "../../fdc3/2.0/mapper"; -import { bringToFront, launch } from "../../launch"; -import { createLogger } from "../../logger-provider"; -import { MANIFEST_TYPES } from "../../manifest-types"; +import { mapToAppMetaData as mapTo12AppMetaData } from "workspace-platform-starter/fdc3/1.2/mapper"; +import { mapToAppMetaData as mapTo20AppMetaData } from "workspace-platform-starter/fdc3/2.0/mapper"; +import { MANIFEST_TYPES } from "workspace-platform-starter/manifest-types"; +import type { EndpointClient } from "workspace-platform-starter/shapes"; import type { AppsForIntent, LaunchPreference, PlatformApp, PlatformAppIdentifier -} from "../../shapes/app-shapes"; -import type { AppMetadata as AppMetadataV1Point2 } from "../../shapes/fdc3-1-2-shapes"; +} from "workspace-platform-starter/shapes/app-shapes"; +import type { AppMetadata as AppMetadataV1Point2 } from "workspace-platform-starter/shapes/fdc3-1-2-shapes"; import type { ApiMetadata, CaptureApiPayload, @@ -33,23 +29,47 @@ import type { IntentTargetMetaData, ProcessedContext, PlatformInteropOverrideOptions -} from "../../shapes/interopbroker-shapes"; -import { formatError, isEmpty, isString, isStringValue, randomUUID, sanitizeString } from "../../utils"; -import { getWindowPositionUsingStrategy } from "../../utils-position"; +} from "workspace-platform-starter/shapes/interopbroker-shapes"; +import type { Logger } from "workspace-platform-starter/shapes/logger-shapes"; +import type { ModuleHelpers } from "workspace-platform-starter/shapes/module-shapes"; +import { + formatError, + isEmpty, + isString, + isStringValue, + randomUUID, + sanitizeString +} from "workspace-platform-starter/utils"; +import { getWindowPositionUsingStrategy } from "workspace-platform-starter/utils-position"; import { AppIntentHelper } from "./app-intent-helper"; import { ClientRegistrationHelper } from "./client-registration-helper"; import { IntentResolverHelper } from "./intent-resolver-helper"; -const logger = createLogger("InteropBroker"); - /** * Get the override constructor for the interop broker (useful if you wish this implementation to be layered with other implementations and passed to the platform's initialization object as part of an array). * @param options The options for the interop broker defined as part of the platform. + * @param logger The logger to use. + * @param helpers A collection of helper methods. * @returns The override constructor to be used in an array. */ export async function getConstructorOverride( - options: PlatformInteropOverrideOptions + options: PlatformInteropOverrideOptions, + logger: Logger, + helpers: ModuleHelpers ): Promise> { + if (!helpers?.getApp || !helpers?.getApps || !helpers.launchApp) { + throw new Error( + "Interop Broker Constructor is missing required helpers. The broker will not function correctly so this error is to flag the issue." + ); + } + const getApp = helpers.getApp; + const getApps = helpers.getApps; + let endpointClient: EndpointClient | undefined; + if (helpers?.getEndpointClient) { + endpointClient = await helpers?.getEndpointClient(); + } + + const launch = helpers.launchApp; return (Base: OpenFin.Constructor) => /** * Extend the InteropBroker to handle intents. @@ -105,14 +125,23 @@ export async function getConstructorOverride( "Interop connection being made by the following identity. About to verify connection", id ); - const response = await connectionProvider.isConnectionValid(id, payload, { type: "broker" }); - if (response.isValid) { - logger.info("Connection validation request was validated and is valid."); - await this._clientRegistrationHelper.clientConnectionRegistered(id, payload as CaptureApiPayload); - } else { - logger.warn(`Connection request from ${JSON.stringify(id)} was validated and rejected.`); + const apiPayload: CaptureApiPayload = payload as CaptureApiPayload; + if (!isEmpty(helpers.isConnectionValid)) { + const response = await helpers.isConnectionValid(id, payload, { type: "broker" }); + if (response.isValid) { + logger.info("Connection validation request was validated and is valid."); + await this._clientRegistrationHelper.clientConnectionRegistered(id, apiPayload); + } else { + logger.warn(`Connection request from ${JSON.stringify(id)} was validated and rejected.`); + } + return response.isValid; + } + // we have not been provided with a means to validate the connection so fallback to default behavior and register the connection + const isValid = await super.isConnectionAuthorized(id, payload); + if (isValid) { + await this._clientRegistrationHelper.clientConnectionRegistered(id, apiPayload); } - return response.isValid; + return isValid; } /** @@ -198,7 +227,7 @@ export async function getConstructorOverride( const isFDC32 = apiVersion?.type === "fdc3" && apiVersion.version === "2.0"; const mappedIntents = intents.map((entry) => ({ intent: entry.intent, - apps: entry.apps.map((app) => { + apps: entry.apps.map((app: PlatformApp) => { let resultType: string | undefined; const listensFor = app?.interop?.intents?.listensFor; if (!isEmpty(listensFor) && !isEmpty(listensFor[entry.intent.name])) { @@ -247,7 +276,7 @@ export async function getConstructorOverride( const isFDC32 = apiVersion?.type === "fdc3" && apiVersion.version === "2.0"; const response = { intent: result.intent, - apps: result.apps.map((app) => { + apps: result.apps.map((app: PlatformApp) => { let resultType: string | undefined; const listensFor = app?.interop?.intents?.listensFor; if (!isEmpty(listensFor) && !isEmpty(listensFor[result.intent.name])) { @@ -621,7 +650,7 @@ export async function getConstructorOverride( if (!isEmpty(bounds)) { launchPreference = { bounds }; } - platformIdentities = await launch(requestedApp, launchPreference); + platformIdentities = await launch(requestedApp?.appId, launchPreference); } else { focusApp = true; } @@ -684,8 +713,8 @@ export async function getConstructorOverride( } if (!isEmpty(appId)) { - if (focusApp && !isEmpty(platformIdentities)) { - await bringToFront(requestedApp, platformIdentities); + if (focusApp && !isEmpty(platformIdentities) && !isEmpty(helpers?.bringAppToFront)) { + await helpers.bringAppToFront(requestedApp, platformIdentities); } return { appId, instanceId }; } @@ -909,7 +938,7 @@ export async function getConstructorOverride( if (!isEmpty(bounds)) { launchPreference = { bounds }; } - platformIdentities = await launch(app, launchPreference); + platformIdentities = await launch(app.appId, launchPreference); if (!platformIdentities?.length) { throw new Error(ResolveError.IntentDeliveryFailed); } @@ -937,7 +966,9 @@ export async function getConstructorOverride( await super.setIntentTarget(intent, target); if (existingInstance) { try { - await bringToFront(app, [target]); + if (helpers.bringAppToFront) { + await helpers.bringAppToFront(app, [target]); + } } catch (bringToFrontError) { logger.warn( `There was an error bringing app: ${target.appId}, and instance ${target.instanceId} with name: ${target.name} to front.`, @@ -1370,12 +1401,18 @@ export async function getConstructorOverride( * @returns The processed context. */ private async processContext(context: OpenFin.Context): Promise { + if (isEmpty(endpointClient)) { + return context; + } const endpointId = `interopbroker.process.${context.type}`; - if (hasEndpoint(endpointId)) { + if (endpointClient.hasEndpoint(endpointId)) { logger.info(`Processing context ${context.type} with endpoint ${endpointId}`); - const processedContext = await requestResponse(endpointId, { - context - }); + const processedContext = await endpointClient.requestResponse( + endpointId, + { + context + } + ); if (processedContext?.context) { return processedContext?.context; } diff --git a/how-to/workspace-platform-starter/client/src/modules/interop-override/wps-interop-override/index.ts b/how-to/workspace-platform-starter/client/src/modules/interop-override/wps-interop-override/index.ts new file mode 100644 index 0000000000..8a9c18e27b --- /dev/null +++ b/how-to/workspace-platform-starter/client/src/modules/interop-override/wps-interop-override/index.ts @@ -0,0 +1,9 @@ +import type { ModuleImplementation, ModuleTypes } from "workspace-platform-starter/shapes/module-shapes"; +import { WpsInteropOverride } from "./interop-override"; + +/** + * Define the entry points for the module. + */ +export const entryPoints: { [type in ModuleTypes]?: ModuleImplementation } = { + interopOverride: new WpsInteropOverride() +}; diff --git a/how-to/workspace-platform-starter/client/src/modules/interop-override/wps-interop-override/interop-override.ts b/how-to/workspace-platform-starter/client/src/modules/interop-override/wps-interop-override/interop-override.ts new file mode 100644 index 0000000000..869804bdd4 --- /dev/null +++ b/how-to/workspace-platform-starter/client/src/modules/interop-override/wps-interop-override/interop-override.ts @@ -0,0 +1,74 @@ +// eslint-disable-next-line max-classes-per-file +import type OpenFin from "@openfin/core"; +import type { + PlatformInteropOverride, + PlatformInteropOverrideOptions +} from "workspace-platform-starter/shapes/interopbroker-shapes"; +import type { Logger, LoggerCreator } from "workspace-platform-starter/shapes/logger-shapes"; +import type { ModuleDefinition, ModuleHelpers } from "workspace-platform-starter/shapes/module-shapes"; +import { getConstructorOverride as wpsConstructorOverride } from "./broker/wps-interop-override"; +import type { WpsInteropOverrideOptions } from "./shapes"; +/** + * Implementation for the wps interop override interop override. + */ +export class WpsInteropOverride implements PlatformInteropOverride { + /** + * The module definition including settings. + * @internal + */ + private _definition: ModuleDefinition | undefined; + + /** + * The logger for displaying information from the module. + * @internal + */ + private _logger?: Logger; + + /** + * Helper methods for the module. + * @internal + */ + private _helpers: ModuleHelpers | undefined; + + /** + * Initialize the module. + * @param definition The definition of the module from configuration include custom options. + * @param loggerCreator For logging entries. + * @param helpers Helper methods for the module to interact with the application core. + * @returns Nothing. + */ + public async initialize( + definition: ModuleDefinition, + loggerCreator: LoggerCreator, + helpers: ModuleHelpers + ): Promise { + this._definition = definition; + const loggerName = definition.data?.loggerName ?? "WpsInteropOverride"; + this._logger = loggerCreator(loggerName); + this._helpers = helpers; + + this._logger.info("Initializing"); + } + + /** + * Close down any resources being used by the module. + * @returns Nothing. + */ + public async closedown(): Promise { + this._logger?.info("Closedown"); + } + + /** + * Get the override constructor for the interop broker (useful if you wish this implementation to be layered with other implementations and passed to the platform's initialization object as part of an array). + * @param options The options for the interop broker defined as part of the platform. + * @returns The override constructor to be used in an array. + */ + public async getConstructorOverride( + options: PlatformInteropOverrideOptions + ): Promise> { + if (!this._helpers || !this._logger) { + throw new Error("Module not initialized"); + } + return wpsConstructorOverride(options, this._logger, this._helpers); + } +} diff --git a/how-to/workspace-platform-starter/client/src/modules/interop-override/wps-interop-override/shapes.ts b/how-to/workspace-platform-starter/client/src/modules/interop-override/wps-interop-override/shapes.ts new file mode 100644 index 0000000000..b4999031b8 --- /dev/null +++ b/how-to/workspace-platform-starter/client/src/modules/interop-override/wps-interop-override/shapes.ts @@ -0,0 +1,7 @@ +/** + * Options for the wps interop override interop override. + */ +export interface WpsInteropOverrideOptions { + /** The name to use when logging out messages */ + loggerName?: string; +} diff --git a/how-to/workspace-platform-starter/client/types/module/shapes/interopbroker-shapes.d.ts b/how-to/workspace-platform-starter/client/types/module/shapes/interopbroker-shapes.d.ts index 91a72491da..dd9f497026 100644 --- a/how-to/workspace-platform-starter/client/types/module/shapes/interopbroker-shapes.d.ts +++ b/how-to/workspace-platform-starter/client/types/module/shapes/interopbroker-shapes.d.ts @@ -34,14 +34,6 @@ export type PlatformInteropOverrideOptions = Omit; /** * Get the current platform. * @returns The current platform. @@ -142,14 +150,29 @@ export interface ModuleHelpers { * @returns dialog client. */ getDialogClient?(): Promise; + /** + * If the platform has been configured to list supported connections then this API can provide a way of validating the connection. + * @param identity The identity of the connection. + * @param payload The payload to validate if provided. + * @param options The options for the validation (provides additional information such as the type of connection that is trying to be made). + * @returns The response from the validation. + */ + isConnectionValid?( + identity: OpenFin.Identity, + payload?: unknown, + options?: ConnectionValidationOptions + ): Promise; /** * If available, this function lets you request the launch of an application that is available to this platform and * the current user. * @param appId The id of the application that is registered against the currently running platform * @param launchPreference If the app supports launch preferences then these can be passed. - * @returns Nothing. + * @returns An array of the platform identities that related from the launch or nothing if nothing was launched. */ - launchApp?(appId: string, launchPreference?: UpdatableLaunchPreference): Promise; + launchApp?( + appId: string, + launchPreference?: UpdatableLaunchPreference + ): Promise; /** * Launch a page in the workspace. * @param pageId The page to launch. diff --git a/how-to/workspace-platform-starter/client/webpack.config.js b/how-to/workspace-platform-starter/client/webpack.config.js index 80b8095c7f..baebcbb8c4 100644 --- a/how-to/workspace-platform-starter/client/webpack.config.js +++ b/how-to/workspace-platform-starter/client/webpack.config.js @@ -929,6 +929,34 @@ const configs = [ experiments: { outputModule: true } + }, + { + entry: './client/src/modules/interop-override/wps-interop-override/index.ts', + devtool: 'inline-source-map', + module: { + rules: [ + { + test: /\.tsx?$/, + use: 'ts-loader', + exclude: /node_modules/ + } + ] + }, + resolve: { + extensions: ['.tsx', '.ts', '.js'], + alias + }, + externals: { fin: 'fin' }, + output: { + filename: 'wps-interop-override.bundle.js', + library: { + type: 'module' + }, + path: path.resolve(__dirname, '..', 'public', 'js', 'modules', 'interop-override') + }, + experiments: { + outputModule: true + } } ]; diff --git a/how-to/workspace-platform-starter/docs/how-to-customize-your-interop-broker.md b/how-to/workspace-platform-starter/docs/how-to-customize-your-interop-broker.md index e10b38eff3..4c428bd9e4 100644 --- a/how-to/workspace-platform-starter/docs/how-to-customize-your-interop-broker.md +++ b/how-to/workspace-platform-starter/docs/how-to-customize-your-interop-broker.md @@ -4,32 +4,27 @@ # How To Customize Your Interop Broker -Workspace Platform Starter includes a default [interop broker override](../client/src/framework/platform/broker/interop-override.ts) that includes support for FDC3 2.0 and intents as well as context. If has been built to support interop with support for the Platform Apps format used by Workspace Platform Starter (directories can still use the FDC3 1.2 & 2.0 format as these are mapped internally to the PlatformApp format). +Workspace Platform Starter includes a default interop broker override that includes support for FDC3 2.0 and intents as well as context. If has been built to support interop with support for the Platform Apps format used by Workspace Platform Starter (directories can still use the FDC3 1.2 & 2.0 format as these are mapped internally to the PlatformApp format). OpenFin Workspace 17.4+ lets you specify an array of interop overrides that can be layered on top of each other so that different overrides can add custom behavior. -Workspace Platform Starter 17.4 supports this ability by adding two new settings to the platformProvider.interop settings. +Workspace Platform Starter 17.4 supports this ability by adding module support to platformProvider.interop. -## moduleInheritance +## default workspace platform interop override is now a module -This setting lets you how we should treat the interop overrides defined in the modules array: +We have broken the interop broker from the main codebase so that it is now a module (this is similar to when we took our opinionated app, page and workspace Home integrations out of the framework and made them into optional modules). The new module can be found here: [wps-interop-override](../client/src/modules/interop-override/wps-interop-override/). -- derived (default) - Your interop overrides will be derived from our default interop override and your modules will use our implementation as a base. -- base - Your interop overrides will be the base class for our default interop override and when we call super we are calling your module implementations. -- standalone - Your modules do not use our default implementation and will be responsible for InteropBroker functionality (extending from the default InteropBroker provided by the OpenFin Workspace Platform NPM Package) +If you wish to keep the existing interop broker support you will need to ensure that the wps interop override is added to the new modules array (we have added it to our sample manifests but if you have your own settings or manifest then you will need to add this support to your setup). You can see this in platformProvider.interop section of the [manifest.fin.json](../public/manifest.fin.json) and [settings.json](../public/settings.json) files (an example is shown below). -```json -"platformProvider": { - ... - "interop": { - "moduleInheritance": "derived" - } -} -``` +The module helpers have been updated to include bringAppToFront and isConnectionValid to the helpers (as these are features used by the default broker implementation that may be useful if you are implementing your own broker or just extending the broker through additional interop overrides). + +If you do not include our default implementation in the modules array then you will have the default implementation from the Workspace Platform NPM package and you will be responsible for customizing your Interop Broker behavior. ## modules -These are interop modules that provide custom interop broker logic. If there are more than one then they will extend each other (the earlier entries will act as the base for subsequent entries). By default the first module (and subsequently the ones that follow) will use our default interop override as a base (this can be configured as described above through the module inheritance). +The ability to specify an [array of interop override constructors](https://cdn.openfin.co/docs/javascript/stable/interfaces/OpenFin.InitPlatformOptions.html#interopOverride) was introduced in the v33 release of the OpenFin runtime and is now exposed to your workspace platform in version 17.4 of workspace. + +These are interop modules that provide custom interop broker logic. If there are more than one then they will extend each other (the earlier entries will act as the base for subsequent entries). If you wish to use the Workspace Platform Starter interop override module as a base for your interop overrides so it should be at the start of the modules array. Here is an extract from [manifest.fin.json](../public/manifest.fin.json): ```json "platformProvider": { @@ -37,67 +32,60 @@ These are interop modules that provide custom interop broker logic. If there are "interop": { "modules": [ { - "enabled": true, - "id": "my-interop-override", - "url": "http://localhost:8080/js/modules/interop-override/my-interop-override.bundle.js", - "data": { - "customSetting": "a custom setting that will be passed to the interop override module for use" + "id": "wps-interop-override", + "icon": "http://localhost:8080/favicon.ico", + "title": "Workspace Platform Starter Interop Broker", + "description": "This is the implementation included in workspace platform starter but it is now exposed as a module to allow for easy replacement.", + "enabled": true, + "url": "http://localhost:8080/js/modules/interop-override/wps-interop-override.bundle.js", + "data": { + "loggerName": "WpsInteropOverride" + } } - } ] } } ``` -- Also added the option of changing when/if the Workspace Platform Starter default interop override runs: - - after (default) - run the Workspace Platform Starter default broker implementation after your interop override modules (so the default implementation is the base class) - - before - run the Workspace Platform Starter default broker implementation before your interop override modules (so your interop overrides are the base class for our default implementation) - - never - never use the Workspace Platform Starter default implementation (So you will be responsible for adding full interop broker support through your modules) +If you wish to keep the default behavior you will need to ensure your settings service or manifest includes the modules array with a reference to the wps-interop-override.bundle.js file. -## Analytics Provider +## endpoint client -You can configure where analytics should be sent by using our Analytics Provider. You can then specify 1 or more modules (see [how to add a module](./how-to-add-a-module.md)) that will receive the analytic events and any config you specify. We have created a [console analytics module](../client/src/modules/analytics/console/) and configured it in [manifest.fin.json](../public/manifest.fin.json) and [second.manifest.fin.json's settings.json file](../public/settings.json). It isn't configured in our [third.manifest.fin.json](../public/third.manifest.fin.json) to show that this is an optional feature. +The default implementation checks to see if there are endpoints that should be used for enriching a context object. It does this by checking to see if there are endpoints with the context type available e.g. `interopbroker.process.${context.type}`. + +In order for the wps interop override module to be able to use endpoints (just like other modules) it will need to be added to the endpointClient list in the endpointProvider settings. An example can be seen in [manifest.fin.json](../public/manifest.fin.json): ```json -"analyticsProvider": { - "modules": [ - { - "enabled": true, - "id": "analytics.console", - "url": "http://localhost:8080/js/modules/analytics/console.bundle.js", - "data": { - "eventLogLevel": "info" +"endpointProvider": { + ... + "endpointClients": { + "clientOptions": [ + { + "enabled": true, + "id": "wps-interop-override", + "endpointIds": [ + "*" + ] } - } - ] - } -``` - -## Platform Override - -We call the analytics provider from the [platform override](../client/src/framework/platform/platform-override.ts) file: - -```javascript -public async handleAnalytics(events: AnalyticsEvent[]) { - // we call the analytics provider from here and pass the events generated by the workspace components. -} + ] + } + }, ``` ## 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: +You can generate the scaffold for a new module by using the following command line, where "my interop override" is the name you want to give your module: ```shell -npm run generate-module analytics "My Analytics" +npm run generate-module interopOverride "my interop override" ``` -This will generate the code in the modules/analytics folder, add an entry into webpack to build it, and add it to the manifest so that the module is loaded. +This will generate the code in the modules/interopOverride folder, add an entry into webpack to build it, and add it to the manifest so that the module is loaded (via platformProvider.interop.modules). + +This now opens up the capability to add your own logic to the default interop broker (e.g. when setContext is called) and then calling the base class to keep the existing behavior. Interop Override Modules can be passed custom settings through the module definition's data property and it will be passed the settings entered in platformProvider.interop so your implementation can re-use those settings. ## Source Reference -- [Analytics](../client/src/framework/analytics.ts) -- [PlatformAnalyticsEvent](../client/src/framework/shapes/analytics-shapes.ts) -- [Console Analytics Module](../client/src/modules/analytics/console/) -- [Platform Override](../client/src/framework/platform/platform-override.ts) +- [Default Workspace Starter Interop Override Module](../client/src/modules/interop-override/wps-interop-override/) [<- Back to Table Of Contents](../README.md) diff --git a/how-to/workspace-platform-starter/public/manifest.fin.json b/how-to/workspace-platform-starter/public/manifest.fin.json index 639207925c..af33b9d74d 100644 --- a/how-to/workspace-platform-starter/public/manifest.fin.json +++ b/how-to/workspace-platform-starter/public/manifest.fin.json @@ -165,7 +165,19 @@ } ] }, - "modules": [] + "modules": [ + { + "id": "wps-interop-override", + "icon": "http://localhost:8080/favicon.ico", + "title": "Workspace Platform Starter Interop Broker", + "description": "This is the implementation included in workspace platform starter but it is now exposed as a module to allow for easy replacement.", + "enabled": true, + "url": "http://localhost:8080/js/modules/interop-override/wps-interop-override.bundle.js", + "data": { + "loggerName": "WpsInteropOverride" + } + } + ] } }, "appProvider": { @@ -329,6 +341,11 @@ "enabled": true, "id": "workspaces-share", "endpointIds": ["share-get", "share-set"] + }, + { + "enabled": true, + "id": "wps-interop-override", + "endpointIds": ["*"] } ] } diff --git a/how-to/workspace-platform-starter/public/pack.manifest.fin.json b/how-to/workspace-platform-starter/public/pack.manifest.fin.json index d02ad1b102..fcf0413bf7 100644 --- a/how-to/workspace-platform-starter/public/pack.manifest.fin.json +++ b/how-to/workspace-platform-starter/public/pack.manifest.fin.json @@ -159,7 +159,19 @@ } ] }, - "modules": [] + "modules": [ + { + "id": "wps-interop-override", + "icon": "http://localhost:8080/favicon.ico", + "title": "Workspace Platform Starter Interop Broker", + "description": "This is the implementation included in workspace platform starter but it is now exposed as a module to allow for easy replacement.", + "enabled": true, + "url": "http://localhost:8080/js/modules/interop-override/wps-interop-override.bundle.js", + "data": { + "loggerName": "WpsInteropOverride" + } + } + ] } }, "appProvider": { diff --git a/how-to/workspace-platform-starter/public/schemas/settings.schema.json b/how-to/workspace-platform-starter/public/schemas/settings.schema.json index 16ecc70ba3..80f71bf26a 100644 --- a/how-to/workspace-platform-starter/public/schemas/settings.schema.json +++ b/how-to/workspace-platform-starter/public/schemas/settings.schema.json @@ -3971,15 +3971,6 @@ "additionalProperties": false, "description": "Options for the platform interop broker.", "properties": { - "defaultBrokerStrategy": { - "description": "The platform includes a default broker override and this setting allows you to control when it is called.\nIf you wish to have the default interop override execute after your logic (it is the base) then set this to \"after\" (the default if not set).\nIf you wish to have the default interop override execute before your logic (your modules are the base) then set this to \"before\".\nIf you wish to just use your modules then you can specify \"never\" and the default interop override will not execute anywhere (Please note this\nwill mean you will be responsible for implementing all interop broker logic beyond the base runtime implementation).", - "enum": [ - "after", - "before", - "never" - ], - "type": "string" - }, "intentOptions": { "$ref": "#/definitions/IntentOptions", "description": "Options related to the way this platform supports intents" diff --git a/how-to/workspace-platform-starter/public/schemas/wps.manifest.schema.json b/how-to/workspace-platform-starter/public/schemas/wps.manifest.schema.json index 08b3457d57..e977551ea7 100644 --- a/how-to/workspace-platform-starter/public/schemas/wps.manifest.schema.json +++ b/how-to/workspace-platform-starter/public/schemas/wps.manifest.schema.json @@ -3682,11 +3682,6 @@ "additionalProperties": false, "description": "Options for the platform interop broker.", "properties": { - "defaultBrokerStrategy": { - "description": "The platform includes a default broker override and this setting allows you to control when it is called.\nIf you wish to have the default interop override execute after your logic (it is the base) then set this to \"after\" (the default if not set).\nIf you wish to have the default interop override execute before your logic (your modules are the base) then set this to \"before\".\nIf you wish to just use your modules then you can specify \"never\" and the default interop override will not execute anywhere (Please note this\nwill mean you will be responsible for implementing all interop broker logic beyond the base runtime implementation).", - "enum": ["after", "before", "never"], - "type": "string" - }, "intentOptions": { "$ref": "#/definitions/IntentOptions", "description": "Options related to the way this platform supports intents" diff --git a/how-to/workspace-platform-starter/public/settings.json b/how-to/workspace-platform-starter/public/settings.json index 88e64571a7..cc852c736a 100644 --- a/how-to/workspace-platform-starter/public/settings.json +++ b/how-to/workspace-platform-starter/public/settings.json @@ -94,7 +94,20 @@ "contexts": ["fdc3.instrument"] } ] - } + }, + "modules": [ + { + "id": "wps-interop-override", + "icon": "http://localhost:8080/favicon.ico", + "title": "Workspace Platform Starter Interop Broker", + "description": "This is the implementation included in workspace platform starter but it is now exposed as a module to allow for easy replacement.", + "enabled": true, + "url": "http://localhost:8080/js/modules/interop-override/wps-interop-override.bundle.js", + "data": { + "loggerName": "WpsInteropOverride" + } + } + ] } }, "appProvider": { @@ -275,7 +288,31 @@ "logProcessedContext": true } } - ] + ], + "endpointClients": { + "clientOptions": [ + { + "enabled": true, + "id": "default-workspace", + "endpointIds": ["get-default-workspace", "set-default-workspace"] + }, + { + "enabled": true, + "id": "pages-share", + "endpointIds": ["share-get", "share-set"] + }, + { + "enabled": true, + "id": "workspaces-share", + "endpointIds": ["share-get", "share-set"] + }, + { + "enabled": true, + "id": "wps-interop-override", + "endpointIds": ["*"] + } + ] + } }, "browserProvider": { "defaultWindowOptions": { diff --git a/how-to/workspace-platform-starter/public/third.manifest.fin.json b/how-to/workspace-platform-starter/public/third.manifest.fin.json index 1dcd5e1556..05a4891d87 100644 --- a/how-to/workspace-platform-starter/public/third.manifest.fin.json +++ b/how-to/workspace-platform-starter/public/third.manifest.fin.json @@ -84,7 +84,6 @@ }, "platformProvider": { "rootUrl": "http://localhost:8080", - "sharing": true, "interop": { "intentResolver": { "url": "http://localhost:8080/common/windows/intents/instance-picker.html", @@ -153,7 +152,19 @@ } ] }, - "modules": [] + "modules": [ + { + "id": "wps-interop-override", + "icon": "http://localhost:8080/favicon.ico", + "title": "Workspace Platform Starter Interop Broker", + "description": "This is the implementation included in workspace platform starter but it is now exposed as a module to allow for easy replacement.", + "enabled": true, + "url": "http://localhost:8080/js/modules/interop-override/wps-interop-override.bundle.js", + "data": { + "loggerName": "WpsInteropOverride" + } + } + ] } }, "appProvider": {