Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Namespace (rename) iframe messages in FDC3 for the web to avoid potential conflicts #1375

Merged
merged 5 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 42 additions & 41 deletions docs/api/ref/GetAgent.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ The `getAgent()` function is the recommended way for **web applications** to con
<TabItem value="ts" label="TypeScript">

```ts
import { getAgent, DesktopAgent } from "@finos/fdc3";
import { getAgent, DesktopAgent, AgentError } from "@finos/fdc3";

try {
const desktopAgent: DesktopAgent = await getAgent();
Expand Down Expand Up @@ -42,7 +42,7 @@ try {

The `getAgent()` function allows web applications to retrieve an FDC3 Desktop Agent API interface to work with, whether they are running in an environment that supports a Desktop Agent Preload (a container-injected API implementation) or a Desktop Agent Proxy (a Browser-based Desktop Agent running in another window or frame). The behavior of `getAgent()` is defined by the [FDC3 Web Connection Protocol (WCP)](../specs/webConnectionProtocol) and communication with a Desktop Agent Proxy in a web-browser is defined by the [Desktop Agent Communication Protocol (DACP)](../specs/desktopAgentCommunicationProtocol). Hence, it allows applications to be written that will work in either scenario without modification or the inclusion of vendor-specific libraries.

If no Desktop Agent is found, a failover function may be supplied by app allowing it to start or otherwise connect to a Desktop Agent (e.g. by loading a proprietary adaptor that returns a `DesktopAgent` implementation or by creating a window or iframe of its own that will provide a Desktop Agent Proxy).
To handle situations where no Desktop Agent is found, a failover function may be supplied by app allowing it to start or otherwise connect to a Desktop Agent (e.g. by loading a proprietary adaptor that returns a `DesktopAgent` implementation or by creating a window or iframe of its own that will provide a Desktop Agent Proxy).

The definition of the `getAgent()` function is as follows:

Expand All @@ -59,46 +59,48 @@ A small number of arguments are accepted that can affect the behavior of `getAge
* @typedef {Object} GetAgentParams Type representing parameters passed to the
* getAgent function.
*
* @property {string} identityUrl The app's current URL is normally sent to
* a web-based desktop agent to help establish its identity. This property
* may be used to override the URL sent (to handle situations where an app's
* URL is not sufficiently stable to use for identity purposes). The URL set
* MUST match the origin of the application (scheme, hostname, and port) or
* it will be ignored. If not specified, the app's current URL will be used.
* @property {number} timeoutMs Number of milliseconds to allow for an FDC3
* implementation to be found before calling the failover function or
* rejecting (default 750). Note that the timeout is cancelled as soon as a
* Desktop Agent is detected. There may be additional set-up steps to perform
* which will happen outside the timeout.
*
* @property {number} timeout Number of milliseconds to allow for an fdc3
* implementation to be found before calling the failover function or
* rejecting (default 750). Note that the timeout is cancelled as soon as a
* Desktop Agent is detected. There may be additional set-up steps to perform
* which will happen outside the timeout.
* @property {string} identityUrl The app's current URL is normally sent to
* a web-based desktop agent to help establish its identity. This property
* may be used to override the URL sent (to handle situations where an app's
* URL is not sufficiently stable to use for identity purposes, e.g. due to
* client-side route changes when navigating within the app). The URL set MUST
* match the origin of the application (scheme, hostname, and port) or it will
* be ignored. If not specified, the app's current URL will be used.
*
* @property {boolean} channelSelector Flag indicating that the application
* needs access to a channel selector UI (i.e. because it supports User Channels
* and does not implement its own UI for selecting channels). Defaults to true.
* MAY be ignored by Desktop Agent Preload (container) implementations.
* @property {boolean} channelSelector Flag indicating that the application
* needs access to a channel selector UI (i.e. because it supports User Channels
* and does not implement its own UI for selecting channels). Defaults to
* `true`. MAY be ignored by Desktop Agent Preload (container) implementations.
*
* @property {boolean} intentResolver Flag indicating that the application
* @property {boolean} intentResolver Flag indicating that the application
* needs access to an intent resolver UI (i.e. because it supports raising one
* or more intents and and does not implement its own UI for selecting target
* apps. Default to `true`. MAY be ignored by Desktop Agent Preload (container)
* or more intents and and does not implement its own UI for selecting target
* apps). Defaults to `true`. MAY be ignored by Desktop Agent Preload (container)
* implementations.
*
* @property {boolean} dontSetWindowFdc3 For backwards compatibility, `getAgent`
* will set a reference to the Desktop Agent implementation at `window.fdc3`
* if one does not already exist, and will fire the `fdc3Ready` event. Setting
* this flag to `true` will inhibit that behavior, leaving `window.fdc3` unset.
*
* @property {function} failover An optional function that provides a
* means of connecting to or starting a Desktop Agent, which will be called
* if no Desktop Agent is detected. Must return either a Desktop Agent
* implementation directly (e.g. by using a proprietary adaptor) or a
* WindowProxy (e.g a reference to another window returned by `window.open`
* or an iframe's `contentWindow`) for a window or frame in which it has loaded
* a Desktop Agent or suitable proxy to one that works with FDC3 Web Connection
* and Desktop Agent Communication Protocols.
* @property {boolean} dontSetWindowFdc3 For backwards compatibility, `getAgent`
* will set a reference to the Desktop Agent implementation at `window.fdc3`
* if one does not already exist, and will fire the fdc3Ready event. Defaults to
* `false`. Setting this flag to `true` will inhibit that behavior, leaving
* `window.fdc3` unset.
*
* @property {function} failover An optional function that provides a
* means of connecting to or starting a Desktop Agent, which will be called
* if no Desktop Agent is detected. Must return either a Desktop Agent
* implementation directly (e.g. by using a proprietary adaptor) or a
* WindowProxy (e.g a reference to another window returned by `window.open`
* or an iframe's `contentWindow`) for a window or frame in which it has loaded
* a Desktop Agent or suitable proxy to one that works with FDC3 Web Connection
* and Desktop Agent Communication Protocols.
*/
type GetAgentParams = {
timeout?: number,
timeoutMs?: number,
identityUrl?: string,
channelSelector?: boolean,
intentResolver?: boolean,
Expand All @@ -123,7 +125,6 @@ Example: Decreasing the timeout and providing a failover function

```js
const desktopAgent = await getAgent({
appId: "myApp@yourorg.org",
timeout: 250,
failover: async (params: GetAgentParams) => {
// return WindowProxy | DesktopAgent
Expand All @@ -135,7 +136,7 @@ The failover function allows an application to provide a backup mechanism for co

:::note

If you wish to _completely override FDC3s standard mechanisms_, then do not use a failover function. Instead, simply skip the `getAgent()` call and provide your own DesktopAgent object.
If you wish to _completely override FDC3's standard discovery mechanisms_, then do not use a failover function. Instead, simply skip the `getAgent()` call and provide your own DesktopAgent object.

:::

Expand Down Expand Up @@ -208,13 +209,13 @@ enum WebDesktopAgentType {

/** Denotes Desktop Agents that run (or provide an interface)
* within a parent window or frame, a reference to which
* will be found at `window.opener`, `window.parent` or
* `window.parent.opener`. */
* will be found at `window.opener`, `window.parent`,
* `window.parent.opener` etc. */
PROXY_PARENT = "PROXY_PARENT",

/** Denotes Desktop Agents that are connected to by loading
* a URL into a iframe whose URL was returned by a parent
* window or frame. */
/** Denotes Desktop Agents that are connected to by loading a URL
* into a hidden iframe whose URL was returned by a parent window
* or frame. */
PROXY_URL = "PROXY_URL",

/** Denotes a Desktop Agent that was returned by a failover
Expand Down
53 changes: 30 additions & 23 deletions docs/api/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ Since version 1.2 of the FDC3 Standard it may do so via the [`fdc3.getInfo()`](r
<TabItem value="ts" label="TypeScript/JavaScript">

```ts
import {compareVersionNumbers, versionIsAtLeast} from '@finos/fdc3';
import { compareVersionNumbers, versionIsAtLeast } from "@finos/fdc3";

if (fdc3.getInfo && versionIsAtLeast(await fdc3.getInfo(), "1.2")) {
await fdc3.raiseIntentForContext(context);
Expand All @@ -207,8 +207,8 @@ The [`ImplementationMetadata`](ref/Metadata#implementationmetadata) object retur
<TabItem value="ts" label="TypeScript/JavaScript">

```ts
let implementationMetadata = await fdc3.getInfo();
let {appId, instanceId} = implementationMetadata.appMetadata;
const implementationMetadata = await fdc3.getInfo();
const { appId, instanceId } = implementationMetadata.appMetadata;

```

Expand Down Expand Up @@ -274,26 +274,34 @@ await fdc3.raiseIntent("StartChat", context, appIntent.apps[0]);
const appIntent = await fdc3.findIntent("ViewContact", context, "fdc3.contact");
try {
const resolution = await fdc3.raiseIntent(appIntent.intent, context, appIntent.apps[0].name);
const result = await resolution.getResult();
console.log(`${resolution.source} returned ${JSON.stringify(result)}`);
} catch(error) {
console.error(`${resolution.source} returned a result error: ${error}`);
try {
const result = await resolution.getResult();
console.log(`${resolution.source} returned ${JSON.stringify(result)}`);
} catch(resultError: ResultError) {
console.error(`${resolution.source} returned an error: ${resultError.message}`);
}
} catch(resolveError: ResolveError) {
console.error(`${JSON.stringify(appIntent.apps[0])} returned an error: ${resolveError.message}`);
}

//Find apps to resolve an intent and return a channel
const appIntent = await fdc3.findIntent("QuoteStream", context, "channel");
try {
const resolution = await fdc3.raiseIntent(appIntent.intent, context, appIntent.apps[0].name);
const result = await resolution.getResult();
if (result && result.addContextListener) {
result.addContextListener(null, (context) => {
console.log(`received context: ${JSON.stringify(context)}`);
});
} else {
console.log(`${resolution.source} didn't return a channel! Result: ${JSON.stringify(result)}`);
try {
const result = await resolution.getResult();
if (result && result.addContextListener) {
result.addContextListener(null, (context) => {
console.log(`received context: ${JSON.stringify(context)}`);
});
} else {
console.log(`${resolution.source} didn't return a channel! Result: ${JSON.stringify(result)}`);
}
} catch(resultError: ResultError) {
console.error(`${resolution.source} returned an error: ${resultError.message}`);
}
} catch(error) {
console.error(`${resolution.source} returned a result error: ${error}`);
} catch (resolveError: ResolveError) {
console.error(`${JSON.stringify(appIntent.apps[0])} returned an error: ${resolveError.message}`);
}

//Find apps that can perform any intent with the specified context
Expand Down Expand Up @@ -383,7 +391,7 @@ For example, to raise a specific intent:
try {
const resolution = await fdc3.raiseIntent("StageOrder", context);
}
catch (err){ ... }
catch (err: ResolveError) { ... }
```

</TabItem>
Expand Down Expand Up @@ -411,8 +419,7 @@ try {
if (resolution.data) {
const orderId = resolution.data.id;
}
}
catch (err){ ... }
} catch (err: ResolveError) { ... }
```

</TabItem>
Expand Down Expand Up @@ -446,7 +453,7 @@ try {
//some time later
await agent.raiseIntent("UpdateOrder", context, resolution.source);
}
catch (err) { ... }
catch (err: ResolveError) { ... }
```

</TabItem>
Expand Down Expand Up @@ -478,13 +485,13 @@ try {
/* Detect whether the result is Context or a Channel by checking for properties unique to Channels. */
if (result && result.broadcast) {
console.log(`${resolution.source} returned a channel with id ${result.id}`);
} else if (result){
} else if (result) {
console.log(`${resolution.source} returned data: ${JSON.stringify(result)}`);
} else {
console.error(`${resolution.source} didn't return anything`);
}
} catch(error) {
console.error(`${resolution.source} returned a data error: ${error}`);
} catch(err: ResultError) {
console.error(`${resolution.source} returned a data error: ${err.message}`);
}
```

Expand Down
6 changes: 3 additions & 3 deletions docs/api/specs/browserResidentDesktopAgents.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,10 +244,10 @@ User interface iframes are initially injected into the application window with C

and are always displayed with `position: "fixed"` so that they are not part of the document flow.

Implementations of the UIs may then indicate a limited set of CSS to apply to their frame in the initial `iFrameHello` message (when the width and height will be removed if not explicitly set in that message), and later adjust that via `iFrameRestyle`. See the [Controlling injected User Interfaces section](./desktopAgentCommunicationProtocol#controlling-injected-user-interfaces-section) in the DACP specification for more details.
Implementations of the UIs may then indicate a limited set of CSS to apply to their frame in the initial `Fdc3UserInterfaceHello` message (when the width and height will be removed if not explicitly set in that message), and later adjust that via `Fdc3UserInterfaceRestyle`. See the [Controlling injected User Interfaces section](./desktopAgentCommunicationProtocol#controlling-injected-user-interfaces-section) in the DACP specification for more details.

Communication between the `DesktopAgentProxy` and the iframes it injects is achieved via a similar mechanism to that used for communication between an App and the Desktop Agent: a `MessageChannel` is established between the app and iframe, via a `postMessage` sent from the iframe (`iFrameHello`) and responded to by the `DesktopAgentProxy` in the app's window (`iFrameHandshake`), with a `MessagePort` from a `MessageChannel` appended.
Communication between the `DesktopAgentProxy` and the iframes it injects is achieved via a similar mechanism to that used for communication between an App and the Desktop Agent: a `MessageChannel` is established between the app and iframe, via a `postMessage` sent from the iframe (`Fdc3UserInterfaceHello`) and responded to by the `DesktopAgentProxy` in the app's window (`Fdc3UserInterfaceHandshake`), with a `MessagePort` from a `MessageChannel` appended.

A further set of messages are provided for working with the injected user interfaces over their `MessageChannel` as part of the DACP, these are: `iFrameRestyle`, `iFrameDrag`, `iFrameChannels`, `iFrameChannelSelected`, `iFrameResolve` and `iFrameResolveAction`.
A further set of messages are provided for working with the injected user interfaces over their `MessageChannel` as part of the DACP, these are: `Fdc3UserInterfaceRestyle`, `Fdc3UserInterfaceDrag`, `Fdc3UserInterfaceChannels`, `Fdc3UserInterfaceChannelSelected`, `Fdc3UserInterfaceResolve` and `Fdc3UserInterfaceResolveAction`.

See the [Desktop Agent Communication Protocol](./desktopAgentCommunicationProtocol) (DACP) for more details.
Loading